首页 > 解决方案 > awk 浮点不精确

问题描述

我在 Awk 中遇到了无法解决的浮点不精确问题。有一个简单的解决方案吗?

这是我复制浮点不精确问题的示例 Awk 脚本。

BEGIN {
  print "PREC = " PREC
  print "OFMT = " OFMT
  print "CONVFMT = " CONVFMT
  a = 1.2 + 3.4
  b = 8.9 - 4.3
  print "a = " a
  print "b = " b
  if ( a == b )
    print "a == b"
  else
    print "a != b"
  c = 3.2 + 5.4
  d = 9.8 - 1.2
  print "c = " c
  print "d = " d
  if ( c == d )
    print "c == d"
  else
    print "c != d"
}

这是上述脚本的输出。

PREC = 53
OFMT = %.6g
CONVFMT = %.6g
a = 4.6
b = 4.6
a != b
c = 8.6
d = 8.6
c == d

为什么 a != b 即使两者具有相同的值?然而, c == d 工作正常。

我假设 awk 有一些内部浮点不精确。仅供参考,我使用的是 Gawk 4.1.4。

我尝试了 PREC、OFMT 和 CONVFMT 的各种值,但找不到合适的值。

例如,将 OFMT 和 CONVFMT 更改为 %.6f:

PREC = 53
OFMT = %.6f
CONVFMT = %.6f
a = 4.600000
b = 4.600000
a != b
c = 8.600000
d = 8.600000
c == d

例如将 PREC 更改为 16:

PREC = 16
OFMT = %.6g
CONVFMT = %.6g
a = 4.6
b = 4.6
a != b
c = 8.6
d = 8.6
c == d

基本上,我希望在 BEGIN 中进行一些设置,而不是更改浮点算术和比较所在的每个表达式,因为我的实际 Awk 脚本比上面的示例要长得多。

例如,我宁愿不必为每个算术和比较表达式使用 sprintf,也不必在按 1e6 缩放后将每个输入数字转换为整数并将每个输出数字转换为 1e-6。这种方法将非常令人生畏。

仅供参考,输入文件中的浮点数最多有 6 个小数点,但它们可能没有小数点,即它们的范围从 0 到 6 个小数点。

谢谢您的帮助。

氢化物

标签: mathawkfloating-pointcomparisonprecision

解决方案


在这里,更高的精度对你不利。由于某些十进​​制值无法以二进制精确表示,因此您只是将数字等价的限制推向了无法满足的更高精度数字。

例如对于 53 位精度,你得到

1.2 => 1.199999999999999955591079014993738383054733
3.4 => 3.399999999999999911182158029987476766109467
8.9 => 8.900000000000000355271367880050092935562134
4.3 => 4.299999999999999822364316059974953532218933

a = 4.599999999999999644728632119949907064437866
b = 4.600000000000000532907051820075139403343201
a != b

3.2 => 3.200000000000000177635683940025046467781067
5.4 => 5.400000000000000355271367880050092935562134
9.8 => 9.800000000000000710542735760100185871124268
1.2 => 1.199999999999999955591079014993738383054733
c = 8.600000000000001421085471520200371742248535
d = 8.600000000000001421085471520200371742248535
c==d

我的建议是将 设置PREC为更合理的值(基于您的输入数据精度)。我认为 10 是一个很好的折衷方案,代码更改最少。

'BEGIN{PREC=10; ...

注意。如果你问为什么c,d匹配,请注意它们都是 0.2 的倍数的分数,而 a,b 是 0.3。


推荐阅读