不要用浮点数 range,会变得不幸

小知识:浮点数不满足结合律交换律

以下基于:julia 1.7.0-rc2

julia> 1 - 1/7 + 4
4.857142857142858

julia> 1 + 4 - 1/7
4.857142857142857

julia> (1 - 1/7 + 4) == (1 + 4 - 1/7)
false

julia> (1 - 1/7 + 4) - (1 + 4 - 1/7)
8.881784197001252e-16

julia> eps(1 - 1/7 + 4)
8.881784197001252e-16
julia> ( (1:5) .- 1/7 ) == ( collect(1:5) .- 1/7 )
false

julia> (1:5) .- 1/7
0.8571428571428572:1.0:4.857142857142858

julia> collect( (1:5) .- 1/7 )
5-element Vector{Float64}:
 0.8571428571428572
 1.8571428571428572
 2.857142857142857
 3.857142857142857
 4.857142857142858

julia> collect(1:5) .- 1/7
5-element Vector{Float64}:
 0.8571428571428572
 1.8571428571428572
 2.857142857142857
 3.857142857142857
 4.857142857142857

下面这个也许是 bug:

julia> (1:5) .- 1/7
0.8571428571428572:1.0:4.857142857142858

julia> (1:5) .- 1/7 |> collect
5-element Vector{Float64}:
 0.8571428571428572
 1.8571428571428572
 2.857142857142857
 3.857142857142857
 4.857142857142858

julia>

julia> (1 - 1/7):(5 - 1/7)
0.8571428571428572:1.0:3.857142857142857

julia> (1 - 1/7):(5 - 1/7) |> collect
4-element Vector{Float64}:
 0.8571428571428572
 1.8571428571428572
 2.857142857142857
 3.857142857142857

在做并行计算的时候这个部分尤其头疼,无法精确验证结果。

一般这种时候都是手写 range

julia> range(start=1-1/7, stop=5-1/7, length=5) |> collect
5-element Vector{Float64}:
 0.8571428571428572
 1.8571428571428572
 2.8571428571428568
 3.8571428571428568
 4.857142857142857

实际上如果去读其他框架文档的话,也会有类似的问题, 比如说 numpy.arange的文档明确说了

When using a non-integer step, such as 0.1, the results will often not be consistent. It is better to use numpy.linspace for these cases.

1 个赞

按我的理解,浮点数应该是满足交换律,而不满足结合律的?

julia> 1 + (-1/7 + 4) == 1 + (4 - 1/7)
true

julia> 1 + (4 - 1/7) == (1 + 4) - 1/7
false
1 个赞

确实,举的例子实际上是结合律。

可以杠一下相等的定义:
作为 IEEE浮点数,我们有神奇的 NaN
所以有时候也不一定满足交换律

julia> 1 + NaN == NaN + 1
false

julia> 1 + NaN === NaN + 1
true

按照 Handbook of Floating-Point Arithmetic 的说法

When the arithmetic operations are correctly rounded, in any of the four rounding modes, floating-point addition and multiplication remain commutative.

—— [P27] 2.4 Lost or Preserved Properties of the Arithmetic on the Real Numbers

同时脚注指出当语言支持自动融合加乘(Fused multiply-add,FMA)时,对于式子 a * b + c * d ,交换两项的结果也不一定相同。

sf 还提到了次正规数(denormal number)的问题,这就扯到浮点数的标准及硬件实现了。


总的来说,使用严格的相等定义 ===,对于浮点数字面量(不可进一步求值)而不是表达式,是满足交换律 a + b = b + a 的。

2 个赞