fortran66のブログ

fortran について書きます。

浮動小数点数で結合則が成り立たない簡単な例

浮動小数点数では結合則が成り立ちません。すなわち

(a + b) + c /= a + (b + c)

です。

その簡単な例として、単精度実数の 2**24 付近で、数直線の刻み幅が 2 になる所で +1 計算をしてみます。

ソースプログラム

浮動小数点演算の評価を /fp:strict オプションで厳密にしておく必要があります。

    program Console3
      implicit none
      integer :: i
      real :: a, b, c
      i = 2**24  
      a = real(i)
      b = (a + 1.0) + 1.0 
      c =  a + (1.0 + 1.0)
      print '(a, f10.0) ', '   2**24                =', a
      print '(a, f10.0) ', '  (2**24 +  1.0) + 1.0  =', b
      print '(a, f10.0/)', '   2**24 + (1.0  + 1.0) =', c 
!      
      do i = -20, 20
        print '(2i10, f10.0, b33.32, ":", b2.2)', i, 2**24 + i, real(2**24 + i), real(2**24 + i), modulo(i, 4)   
      end do    
    end program Console3

実行結果

2**24 までは数直線が1刻みですが、ここから上は数直線は2刻みになるので、1 を 2 回足しても 2**24 = 16777216 以上には増えません。しかし先に 1 と 1 を足して 2 にしておくと、刻みを1個上がって 16777218 となります。

つまり、結合則が破れていることが分かります。


ところで、数直線が2刻みになったあとは、値は2個ごとに2飛びで増えてゆくのかと思いきや、1:3の割合でちんばに増えてゆきます。これは、見えないビットの0捨1入後にビット列として偶になるように丸める規則のためと思います。

   2**24                = 16777216.
  (2**24 +  1.0) + 1.0  = 16777216.
   2**24 + (1.0  + 1.0) = 16777218.

       -20  16777196 16777196. 01001011011111111111111111101100:00
       -19  16777197 16777197. 01001011011111111111111111101101:01
       -18  16777198 16777198. 01001011011111111111111111101110:10
       -17  16777199 16777199. 01001011011111111111111111101111:11
       -16  16777200 16777200. 01001011011111111111111111110000:00
       -15  16777201 16777201. 01001011011111111111111111110001:01
       -14  16777202 16777202. 01001011011111111111111111110010:10
       -13  16777203 16777203. 01001011011111111111111111110011:11
       -12  16777204 16777204. 01001011011111111111111111110100:00
       -11  16777205 16777205. 01001011011111111111111111110101:01
       -10  16777206 16777206. 01001011011111111111111111110110:10
        -9  16777207 16777207. 01001011011111111111111111110111:11
        -8  16777208 16777208. 01001011011111111111111111111000:00
        -7  16777209 16777209. 01001011011111111111111111111001:01
        -6  16777210 16777210. 01001011011111111111111111111010:10
        -5  16777211 16777211. 01001011011111111111111111111011:11
        -4  16777212 16777212. 01001011011111111111111111111100:00
        -3  16777213 16777213. 01001011011111111111111111111101:01
        -2  16777214 16777214. 01001011011111111111111111111110:10
        -1  16777215 16777215. 01001011011111111111111111111111:11
         0  16777216 16777216. 01001011100000000000000000000000:00
         1  16777217 16777216. 01001011100000000000000000000000:01
         2  16777218 16777218. 01001011100000000000000000000001:10
         3  16777219 16777220. 01001011100000000000000000000010:11
         4  16777220 16777220. 01001011100000000000000000000010:00
         5  16777221 16777220. 01001011100000000000000000000010:01
         6  16777222 16777222. 01001011100000000000000000000011:10
         7  16777223 16777224. 01001011100000000000000000000100:11
         8  16777224 16777224. 01001011100000000000000000000100:00
         9  16777225 16777224. 01001011100000000000000000000100:01
        10  16777226 16777226. 01001011100000000000000000000101:10
        11  16777227 16777228. 01001011100000000000000000000110:11
        12  16777228 16777228. 01001011100000000000000000000110:00
        13  16777229 16777228. 01001011100000000000000000000110:01
        14  16777230 16777230. 01001011100000000000000000000111:10
        15  16777231 16777232. 01001011100000000000000000001000:11
        16  16777232 16777232. 01001011100000000000000000001000:00
        17  16777233 16777232. 01001011100000000000000000001000:01
        18  16777234 16777234. 01001011100000000000000000001001:10
        19  16777235 16777236. 01001011100000000000000000001010:11
        20  16777236 16777236. 01001011100000000000000000001010:00
続行するには何かキーを押してください . . .