为什么使用范围数组没有正确迭代值?

时间:2017-02-01 17:15:47

标签: julia

我遇到了这个问题

julia> Tinit = 0.0
    0.0  

julia> for ii in 1:10
           timeinterval = Tinit:0.05:(Tinit + 0.05)
           println(Tinit + 0.05)
           for item in timeinterval
                   println("+++++++++ ", item)
           end
           Tinit = timeinterval[end]
       end

由于一些奇怪的原因,我无法获得大于0.15

的值

1 个答案:

答案 0 :(得分:11)

您在浮点运算中遇到了一个经典问题:

julia> 0.05 + 0.05
0.1

julia> 0.05 + 0.05 + 0.05
0.15000000000000002

julia> 0.05 + 0.05 + 0.05 + 0.05
0.2

所以你的第一个问题是0.1 + 0.5 == 0.15000000000000002 > 0.15。见Is floating point math broken?。所以你没有得到你希望得到的确切实数。你遇到的第二个问题是你正在构建一个范围:

julia> r = 0.15000000000000002:0.05:0.2
0.15000000000000002:0.05:0.15000000000000002

julia> length(r)
1

julia> r[end]
0.15000000000000002

什么?!?为什么?这是因为你的起点比你预期的要大(你的终点也比你预期的要大,但不是很明显:0.2 == 2.00000000000000011102230246251565404236316680908203125)。特别要注意

julia> (0.2-0.15000000000000002)/0.05
0.9999999999999998

换句话说,你正在构建的范围不够大,不足以得到两分。但是,情况也是如此:

julia> 0.15000000000000002 + 0.05
0.2

可以说,如果您可以添加步骤n次并确切地点击结束点,那么范围应包括该结束点,因此这可能被视为错误(问题归档:#20373) 。但是,我很确定我们可以找到其他情况,你的算法会积累足够的浮点错误。

有几种可能的解决方案:

1)不要进行绝对浮点计算而不是迭代浮点计算。例如,您可以将范围表示为(0:0.05:0.05) + Tinit + (ii-1)*0.05

julia> Tinit = 0.0; for ii in 1:10
           println(Tinit + (ii-1)*0.05 + (0:0.05:0.05))
       end
0.0:0.05:0.05
0.05:0.05:0.1
0.1:0.05:0.15
0.15000000000000002:0.05:0.2
0.2:0.05:0.25
0.25:0.05:0.3
0.30000000000000004:0.05:0.35000000000000003
0.35000000000000003:0.05:0.4
0.4:0.05:0.45
0.45:0.05:0.5

正如您所看到的,这并不能完全解决浮点错误问题,但错误不会从一次迭代累积到下一次迭代。

2)使用精确的整数运算,除以100或任何除数,将该计算降低到最接近的可表示浮点值。由于浮点只用于表示,而不是核心计算,核心计算是正确的,永远不会累积错误。

julia> Tinit = 0.0; for ii in 1:10
           println((Tinit+(ii-1)*5)/100:0.5:(Tinit+ii*5)/100)
       end
0.0:0.5:0.0
0.05:0.5:0.05
0.1:0.5:0.1
0.15:0.5:0.15
0.2:0.5:0.2
0.25:0.5:0.25
0.3:0.5:0.3
0.35:0.5:0.35
0.4:0.5:0.4
0.45:0.5:0.45

正如您所看到的,这实际上完全消除了不精确的问题。