Math 浮点不精确

Math 浮点不精确,math,awk,floating-point,comparison,precision,Math,Awk,Floating Point,Comparison,Precision,我在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 = &quo

我在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个小数点

谢谢你的帮助


HN

浮点数不精确,显示的答案是四舍五入的,与浮点数表示法不完全相同,但相等性测试会计算结果的每一位

举个例子,试着用铅笔和纸把1除以3,得到0.3333。。。直到你的纸用完。现在乘以应该是1.0,对吗?不,你会得到0.9999999

类似地,浮点不能精确表示0.1

通常所做的是比较等式,使其处于某个极限内,称为“ε”

如果(a-b)的绝对值<0.0000001
然后打印“相等”

如果需要以更高的值表示浮点常量 精度高于默认值,并且不能使用命令行指定 PREC,您应该将常量指定为字符串或 有理数,只要可能


在这里,更高的精度对你不利。因为有些十进制值不能用二进制精确表示,所以你只是把数字等价的极限推到了更高精度的数字上,而这是无法满足的

例如,对于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。

所有有限浮点数都是精确的,就像所有整数一样。许多人认为这是一个不精确的数学,请那些人相信。甚至与“epsilon”相比,这个名称也是一个用词不当。@biker:OP只是在做减法运算,还是只是为了向堆栈溢出发帖而构造的一个样本?如果他们在实际应用中有更复杂的算法,我们不知道误差范围是什么。此外,他们还告诉我们,输入数字的精度(小数点后最多6位)有一个限制,但其范围(小数点前有多少位)没有限制。@user14771043:让计算机算术像实数算术一样,没有通用的解决方案。有关于这个主题的全部书籍、课程和论文。对于简单的情况,有简单的解决方案。您尚未很好地指定应用程序,无法推荐解决方案。为什么要从输入中减去读取的值?你为什么要比较它们?这是你唯一做的算术吗?数字能有多大?@HCN:这些操作会产生任意大的错误。很简单,想想你已经知道了9.8−1.2−8.6不等于零。反复将非零误差乘以大于1的值,误差将永远增大。我不知道你在这个文件里做什么。也许你是在减去差额,然后将差额转移到下一行,再乘以某个值(比如复利)。因此,将非零误差乘以每行中的某个值,再乘以1000行,误差可以增长到任意大小。需要对该应用程序进行更好的描述。“最多有6个小数点”-->您能将每个输入按1000000整数进行缩放,然后再进行计算吗?@chux我希望不会像前面所说的那样达到这个目的。除了最终输出,程序还将打印中间文件。所以,我必须在每次打印时都包含转换。这回答了你的问题吗@杰梅斯克总统,波尔克不完全是。我知道这个问题与浮点有关,尽管不完全是如何解决的。我寻找的不是学术上的解释,而是一个简单而优雅的Awk解决方案,而不必显式地处理每个算术表达式的不精确性。我希望脚本语言像AWK可以让用户免于处理像C++这样的编译语言中的繁琐问题,包括类型、声明、数组绑定、无符号VS签名号、最大整数号等,以运行时间开销很小的代价。
'BEGIN{PREC=10; ...