【问题标题】:awk floating point comparison not workingawk 浮点比较不起作用
【发布时间】:2020-08-07 21:29:48
【问题描述】:

我有带有 x1 、 x2 和 x 值的输入文件,我想检查 x 是否是 x1 和 x2 之间的中点。 但是比较失败了。

示例输入文件

x1=20.9280 x2=20.9600 x=20.9440
x1=20.9280 x2=20.9600 x=20.9440
x1=22.7840 x2=22.8160 x=22.8000

awk 命令

awk -F'[ =]' '{ if(($2 + $4)/2 != ($6)) print ($2 + $4)/2, " ", $6;}' sample

输出

20.944   20.9440
20.944   20.9440
22.8   22.8000

由于小数点后有多余的零,比较失败。请帮忙解决。

【问题讨论】:

  • 在进行比较之前,您可以在$6 的末尾用sub(/0+$/, "", $6) 删除0。但一般来说,直接比较浮点数是有风险的,通常会检查两个浮点数之间的绝对差异反对一个小的价值,比如0.00001
  • @EdMorton 我认为 OP 想要打印最后一列与中点计算不匹配的情况,例如 x1=20.9280 x2=20.9600 x=20.9240 (更改示例).. 但是使用有问题的代码,所有三行即使它们在最后一列中都有正确的中点,也被打印出来了。这就是为什么 OP 说他们不希望输出例如有问题的行...
  • @EdMorton 所以,OP 可能只是在寻找 awk -F'[ =]' '{gsub(/\./,"")} ($6*2) != ($2+$4)'
  • @Sundeep 谢谢,我是个白痴,我错过了比较中的!。是的,我在回答中提出了这种转换。
  • 好吧,我从你的回答中复制了它;)顺便说一句,好把戏!

标签: awk floating-point


【解决方案1】:

这是由于所有平台普遍存在的浮点比较问题造成的。

您可以使用此awk 进行浮点数比较,方法是将数字转换为具有 4 个小数点的浮点数:

awk -F'[ =]+' '{avg = sprintf("%.4f", ($2 + $4) / 2)} avg != $6 { print avg, $6 }' file

如果您有gnu awk,那么您可以将精度设置为较小的数字:

awk -M -v PREC=30 -F'[ =]+' '{avg = ($2 + $4) / 2; $6 += 0} avg != $6 { print avg, $6 }' file

【讨论】:

  • 我不经常这么说,但我强烈反对第一种解决方案。由于sprintf 声明,您将创建误报。例如,1.23445 和 1.23455 之间的所有数字都将转换为 1.2345。
  • 是的,我明白你的意思。 sprintf 解决方案仅适用于 non-gnu awk 并且它是 customized 用于 OP 的输入,在所有数字中使用 4 的比例,所以很明显情况 1.234451.23455 此处不存在。
【解决方案2】:

不是真正的 anwser,但确实演示了。您正在比较浮点数,它们不相等。我用printf 替换了print,并用足够的小数替换了修饰符(20,%.20f):

$ awk -F'[ =]' '{
    if(($2 + $4)/2 != ($6)) 
        printf "%.20f %.20f\n", ($2 + $4)/2, $6
}' file

输出:

20.94400000000000261480 20.94399999999999906208
20.94400000000000261480 20.94399999999999906208
22.79999999999999715783 22.80000000000000071054

所以使用sprintf 和适当的修饰符(参见我使用的printf)来控制值。

【讨论】:

    【解决方案3】:

    正如其他人指出的那样,如果您遇到问题,那么您可能只是遇到了常见的浮点算术问题,但由于您的所有输入值都具有相同的精度,您可以摆脱 @ 987654321@s 将输入数字视为整数并乘以 2 而不是除以 2 只是为了保持整数比较:

    $ awk -F'[ =]' '{o=$0; gsub(/\./,"")} ($6*2) == ($2+$4){$0=o; print ($2+$4)/2, $6}' file
    20.944 20.9440
    20.944 20.9440
    22.8 22.8000
    
    $ awk -F'[ =]' '{o=$0; gsub(/\./,"")} ($6*2) != ($2+$4){$0=o; print ($2+$4)/2, $6}' file
    $
    

    【讨论】:

    • 我不推荐使用这个解决方案。它可能适用于目前的情况,但如果数字开始采用不同的“格式”、“范围”、“比例”会怎样
    • 这就是为什么它说since all of your input values have the same precision。它只是针对这种特殊情况的简单解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-10
    相关资源
    最近更新 更多