versioninfo()
Julia Version 1.9.0 Commit 8e630552924 (2023-05-07 11:25 UTC) Platform Info: OS: macOS (arm64-apple-darwin22.4.0) CPU: 8 × Apple M2 WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-14.0.6 (ORCJIT, apple-m1) Threads: 2 on 4 virtual cores
a = Int32(10)
10
b = Int32(2147483647) + Int32(1)
-2147483648
c = typemax(Int32)
2147483647
これからInt32の最大値が2147483647であることがわかり $2^{31}-1=2147483647$ である.
説明のために32bit整数(Int32)を考えると
ビットパターン | 数値 |
---|---|
01111111111111111111111111111111 | 2147483647 |
00000000000000000000000000000010 | 2 |
00000000000000000000000000000001 | 1 |
00000000000000000000000000000000 | 0 |
11111111111111111111111111111111 | -1 |
11111111111111111111111111111110 | -2 |
10000000000000000000000000000000 | -2147483648 |
このような負の数の表現形式を「2の補数形式」と呼ぶ. 32個の各bitが次のような重みをもっていると考えられる.
$$\fbox{$-2^{31}$}\fbox{$2^{30}$}\fbox{$2^{29}$}\,\cdots\fbox{$2^{1}$}\fbox{$2^{0}$}$$2の補数形式の場合,$n$ビットで $-2^{n-1}$〜$2^{n-1}-1$の範囲の数を表現できる.つまり
型 | 表現範囲 |
---|---|
Int8 | -128〜127 |
Int16 | -32768〜32767 |
Int32 | -2147483648〜2147483647 |
Int64 | -9223372036854775808〜9223372036854775807 |
Int128 | -170141183460469231731687303715884105728〜170141183460469231731687303715884105727 |
の範囲の整数が表せる.
2147483647+1
2147483648
9223372036854775807+1
-9223372036854775808
浮動小数点数(Float64/double, Float32/float)は,「浮動小数点形式」と呼ばれる形式で表現できる.
例えば,「$1234.5$」を「$1.2345\times 10^3$」のように, 小数点の位置を1番左の数値と左から2番目の数値の間に移動(「正規化」と呼ぶ)し,それに指数を掛けた形式で数を表現する. この「$1.2345$」の部分を「仮数部」,「$10^3$」の部分(厳密には$~^3$)を「指数部」という.
浮動小数点数は仮数部の長さ,指数部の長さ,基数が2,10,16など,多様な規格が考えられる. そこで1985年にWilliam Kahanが中心となって
IEEE 754: Standard for Binary Floating-Point Arithmetic
という標準規格が制定された.最近では世に出るハードウェアのほぼ全てがこの規格に従っている.
倍精度は,符号($\pm$)に1ビット.指数部に11ビット,仮数部に 52ビットを使う. 全部で64ビット=8バイトである.
$\fbox{1(符号)}\fbox{11(指数部)}\fbox{52(仮数部)}$
数値$x$は
$$x=\pm\; 1.d_1d_2\cdots d_{52}\times 2^{m}=\pm\left(\frac{1}{2^0}+\frac{d_1}{2^1}+\frac{d_2}{2^2}+\cdots+\frac{d_{52}}{2^{52}}\right)2^{e}_{(10)}$$と書ける($-1022\le e\le 1023$,$m$:$e+1023$の2進表現).
例えば,5.25は2進数で書くと $$ 101.01_{(2)} = \left(\frac{1}{2^0}+\frac{0}{2^1}+\frac{1}{2^2}+\frac{0}{2^{3}}+\frac{1}{2^4}\right)\times 2^2_{(10)} $$ であるから,計算機内では
$\fbox{0}\fbox{10000000001}\fbox{0101000000000000000000000000000000000000000000000000}$
のように格納されている。指数部の「1000000001」は、「2+1023=1025」 を2進数にしたもの.
これは正規化数と呼ばれる数の範囲.
実際にJuliaのbitstring関数を使って浮動小数点数を見てみよう.
x = 5.25
println(bitstring(x))
println(bitstring(5.24))
0100000000010101000000000000000000000000000000000000000000000000 0100000000010100111101011100001010001111010111000010100011110110
binx = bitstring(x)
sign = binx[1]; println(sign)
exp = binx[2:12]; println(exp)
frac = binx[13:end]; println(frac)
0 10000000001 0101000000000000000000000000000000000000000000000000
符号はsign = 0で正の数, 指数部はexp-1023=1025-1023=2,
仮数部はfrac=(101000000000000000000000000000000000000000000000000)$_2$で2進数表示され,実際の値は1.3125,
元の浮動小数点数に戻すと
で次のように確かに元の数に戻ることがわかる.
aa = [parse(Int, f) for f in frac];
bb = 2. .^(1:52);
dfrac = 1 + sum(aa ./ bb); println(dfrac)
cc = [parse(Int, f) for f in exp]
dexp = sum(cc .* (2 .^(10:-1:0))) - 1023; println(dexp)
x = (-1)^(parse(Int,sign))*dfrac*2^(dexp); println(x)
1.3125 2 5.25
次に$e$の範囲$-1022\le e\le 1023$に注目する.
$e+1023$が11ビット符号無しの整数なら範囲は$0$〜$2047$であるが,$e$範囲から $e+1023=0$のときと$e+1023=2047$のときが使われていない.
これらは特殊な数を表すのに使われる.それらは
と呼ばれる.
指数部が$e+1023=0$かつ仮数部が0のとき.
$$\pm\; 0.00\cdots 0\times 2^{0}={\pm\left(\frac{0}{2^0}+\frac{0}{2^1}+\frac{0}{2^2}+\cdots+\frac{0}{2^{52}}\right)2^{-1023}}_{(10)}.$$x = 0.0; println(x), println(bitstring(x))
x = -0.0; println(x), println(bitstring(x))
0.0 0000000000000000000000000000000000000000000000000000000000000000 -0.0 1000000000000000000000000000000000000000000000000000000000000000
(nothing, nothing)
$e+1023=2047$かつ仮数部が0のとき,$\pm\infty$を表す.
$$\pm\; 1.00\cdots 0\times 2^{m}={\pm\left(\frac{1}{2^0}+\frac{0}{2^1}+\frac{0}{2^2}+\cdots+\frac{0}{2^{52}}\right)2^{1024}}_{(10)},$$$m$: 2047の2進表現. このときオーバーフローが起こるという.
x = Inf; println(x), println(bitstring(x))
println(-x), println(bitstring(-x))
Inf 0111111111110000000000000000000000000000000000000000000000000000 -Inf 1111111111110000000000000000000000000000000000000000000000000000
(nothing, nothing)
x = Inf; println(bitstring(x))
x = NaN; println(x), println(bitstring(x))
x = Inf - Inf; println(x), println(bitstring(x))
x = Inf / Inf; println(x), println(bitstring(x))
0111111111110000000000000000000000000000000000000000000000000000 NaN 0111111111111000000000000000000000000000000000000000000000000000 NaN 0111111111111000000000000000000000000000000000000000000000000000 NaN 0111111111111000000000000000000000000000000000000000000000000000
(nothing, nothing)
正の正規化数の最大の数は,$e+1023=2046$, かつ仮数部のビットが全て1のとき.
$${\left(\frac{{1}}{2^0}+\frac{1}{2^1}+\frac{1}{2^2}+\cdots+\frac{1}{2^{52}}\right)2^{{1023}}}_{(10)}= 2^{1024}-2^{971}\approx10^{308.25}$$であり,これを1ビットでも超えたら無限大になる.これをオーバーフローという.
x = (2. ^ 53 - 1) * 2. ^ 971
println(x)
println(bitstring(x))
x = (2. ^ 53) * 2. ^ 971
println(x)
println(bitstring(x))
1.7976931348623157e308 0111111111101111111111111111111111111111111111111111111111111111 Inf 0111111111110000000000000000000000000000000000000000000000000000
正の正規化数の最小の数は,$e+1023=1$, かつ仮数部のビットが全て0のとき.
$${\left(\frac{{1}}{2^0}+\frac{0}{2^1}+\frac{0}{2^2}+\cdots+\frac{0}{2^{52}}\right)2^{-1022}}_{(10)}= 2^{-1022}\approx10^{-307.65}$$であり,これを下回るとアンダーフローというが,IEEE 754 では,ここでアンダーフローさせないで仮数部を使った「悪あがき」をする.次の例を見てみよう.
x = 2. ^ (-1022)
println(x)
println(bitstring(x))
y = x-2^(-1074);
println(y)
println(bitstring(y))
2.2250738585072014e-308 0000000000010000000000000000000000000000000000000000000000000000 2.225073858507201e-308 0000000000001111111111111111111111111111111111111111111111111111
指数部が$e+1023=0$かつ仮数部が0でないとき,
仮数部の最初の桁を$0$にして
$$\pm\; 0.d_1d_2\cdots d_{52}\times 2^{0}={\pm\left(\frac{\color{red}0}{2^0}+\frac{d_1}{2^1}+\frac{d_2}{2^2}+\cdots+\frac{d_{52}}{2^{52}}\right)2^{{\color{red}{-1022}}}}_{(10)}.$$という数の表現をする.つまり指数部が$e+1023=1$よりも小さくなったら、正規化しないで指数部は$e+1023=1$のままにする. 代わりに仮数部の最初の桁を$0$にする. このような決まりによって, 非正規化数は文字通り「正規化していない」数となる.
上の例のように正規化数の最小数の最終bitを1だけ減らすと
$${\left(\frac{{0}}{2^0}+\frac{1}{2^1}+\frac{1}{2^2}+\cdots+\frac{1}{2^{52}}\right)2^{-1022}}_{(10)}$$となり,これを正規化すると
$${\left(\frac{{1}}{2^0}+\frac{1}{2^1}+\frac{1}{2^2}+\cdots+\frac{0}{2^{52}}\right)2^{-1023}}_{(10)}$$となって,指数部の下限$-1022\le e$を超えてしまう. そこで,「$2^{-1022}$を下回ったら正規化をやめて指数部を$2^{-1022}$に固定して仮数部の最初の桁を0としてみて格納する」ルールが発動し, 非正規化数での数の表現が始まる. 以下, 1ビットずつ減らしていくと
$${\left(\frac{{0}}{2^0}+\frac{1}{2^1}+\frac{1}{2^2}+\cdots+\frac{1}{2^{52}}\right)2^{-1022}}_{(10)}$$$${\left(\frac{{0}}{2^0}+\frac{1}{2^1}+\frac{1}{2^2}+\cdots+\frac{0}{2^{52}}\right)2^{-1022}}_{(10)}$$$$\vdots$$$$\left(\frac{{0}}{2^0}+\frac{1}{2^1}+\frac{0}{2^2}+\cdots+\frac{0}{2^{52}}\right)2^{-1022}$$$${\left(\frac{{0}}{2^0}+\frac{0}{2^1}+\frac{1}{2^2}+\cdots+\frac{1}{2^{52}}\right)2^{-1022}}_{(10)}$$$$\vdots$$$${\left(\frac{{0}}{2^0}+\frac{0}{2^1}+\frac{0}{2^2}+\cdots+\frac{1}{2^{52}}\right)2^{-1022}}_{(10)}=2^{-1074}\approx10^{-323.31}$$のような数が表現できる.ただし,$2^{-1022}$と$2^{-1074}$の間の数は,本来53ビットあるべき仮数部の長さが52ビット〜1ビットまで減ってしまっており,精度が低下していることに注意が必要である.
x = 2. ^ (-1074)
println(x)
println(bitstring(x))
x = (2. ^ (-1074)) / 2
println(x)
println(bitstring(x))
5.0e-324 0000000000000000000000000000000000000000000000000000000000000001 0.0 0000000000000000000000000000000000000000000000000000000000000000
倍精度(binary64, Float64/double)は
仮数部が0 | 仮数部が0でない | |
---|---|---|
$e+1023=0$ | $\pm0$ | 非正規化数 |
$1\le e+1023\le 2046$ | 正規化数 | 正規化数 |
$e+1023=2047$ | $\pm\infty$ | NaN |
単精度 (binary32, Float32/float) は
仮数部が0 | 仮数部が0でない | |
---|---|---|
$e+127=0$ | $\pm0$ | 非正規化数 |
$1\le e+127\le 254$ | 正規化数 | 正規化数 |
$e+127=255$ | $\pm\infty$ | NaN |
今後,浮動小数点数全体の集合を$\mathbb{F}$と表すことにする.特に断りがなければ,浮動小数点数は倍精度浮動小数点数(64bit)とする.IEEE754ではbinary64とも呼ばれている.
本資料は筆者が学生の頃に精度保証付き数値計算を教えて下さった柏木雅英先生の「数値解析特論」の講義資料が基になっています. また, 以下のような文献・Web ページ等を参考にこの文章は書いています.
(Twitterとかでも度々話題に上がる名著. IEEE754 の制定の年にすでに浮動小数点数に対する注意が詰まっている書籍が出版されている)
(数値解析学の現在最も詳しい教科書. 浮動小数点数についても1章に詳しく記述がある.)
(精度保証付き数値計算の教科書. 浮動小数点数および区間演算に詳しい. この1章が読めたら大したもの)
(IEEE 754 浮動小数点数を細かく紹介し, 丸め誤差の詳細, および区間演算について触れている)
(数値解析の超有名人によるブログ記事, (IEEE754/854)浮動小数点数について端的にまとめられている)