朱莉娅:如何处理浮点精度的准确性

时间:2017-09-06 13:55:02

标签: floating-point julia precision

我遇到了浮点数精度不精确的情况。正如我在下面作为我的代码的简化版本所示,我在等效场景中计算总和,并且在最后的小数中我得到不同的值。这与Is floating point math broken

有关

我想知道是否有办法解决这个问题,例如某种截断?

s1 = zero(Float64)
s = zero(Float64)
for a in 1:10000
    for k in 1:4
        temp=log(1e-5)
        s1+=temp
        s +=temp
    end
end
s2 = zero(Float64)
for a in 1:10000
    for k in 1:4
        temp = log(1e-5)
        s2+=temp
        s+=temp
    end
end

s1+s2 == s     ###FALSE
s < s1+s2      ###TRUE

2 个答案:

答案 0 :(得分:4)

浮点加法不是associative:改变加法的顺序可能会因中间舍入而产生微妙的不同结果。

你可以做几件事:

  1. 使用更准确的求和算法,例如Kahan compensated summation。这在Julia中以sum_kbn
  2. 的形式提供

    julia> sum_kbn(log(1e-5) for a = 1:10000, k = 1:4) -460517.01859880914

    1. 检查大致相等而不是完全相等。正如Gnimuc所提到的,您可以使用isapprox(也可以通过中缀获得)。这会分别接受atolrtol个可选参数来指定绝对和相对容差。
    2. s1+s2 ≈ s

答案 1 :(得分:2)

Float64表示IEEE 754格式的数字。在64位中,尾数占据52位。这意味着大约15个十进制数字的精度。

第一次循环后,s1s应该相同。

差异发生在第二个循环体中。 temp中的数字始终相同。第一次进入第二个循环的循环体时,s几乎为temp * 40000.0。将temp添加到s时,会导致temp的最后4.5位数字丢失。这是差异的原因,因为s2将为零,添加temp将使用temp的所有数字。

顺便说一句,s,s1+s2中没有一个(编程)与temp * 80000.0相同(数学上所有3个数字都应该相同)

因此,为减少数字错误,请遵守规则

  • 更喜欢乘法,它们保持精确度
  • 添加数字越精确越平等。您按照a / ba = abs(A), b = abs(B), a > b, b != 0
  • 的顺序松开精度
  • 减去数字的精度越高,它们越相等。您按照前导常用数字或更准确的位数的顺序松开精度。

这一切都不是julia的具体问题,它将在使用IEEE 754(C / C ++ double,Java double,Julia Float64,..)的所有语言中发生。

s1 = zero(Float64)
s = zero(Float64)
for a in 1:40000
    temp=log(1e-5)
    s1+=temp
    s +=temp 
end
s2 = zero(Float64)
for a in 1:40000
    temp = log(1e-5)
    s2+=temp
    s+=temp
end

sm = 80000.0 * log(1e-5);
println(s1+s2 == s)
println(s < s1+s2)
println(s == sm)
println(s1+s2 == sm)

println(s1+s2)
println(sm)
println(s)

输出

false
true
false
false
-921034.0371971375
-921034.0371976183
-921034.0371986133