10進法で表記した有理数を2進法の小数で表記したときに,有限の桁数で表すことができる有限小数になるための特徴付けを与える.この応用として10進法で表記した有限小数が計算機上で誤差を含む数値かどうかの判定ができる.
例.有理数 と の2進法の小数表記
例えば,10進法で表記した有理数 を2進法の小数で表すと となる.ここで,数の右下に 2 を付すと2進法表記を表すことにする.この場合は小数部が 3 桁となり有限の桁で表せられるので有限小数である.
次に,10進法表記の有理数 を2進数小数で表すと となる(0.1 を二進数に - Wolfram|Alpha).したがって は2進法の小数で表記したとき有限小数とはならない.
応用
有理数 の小数 を計算機上で表現しようとすると上で述べたように2進数の有限小数では表せられないので誤差を含む(一般的な浮動小数点数型の場合).ただし,有限小数でも桁数がその型で収まらない場合は誤差を含む.下に有理数 と を浮動小数点数型で表した結果を載せる.
有理数 5 / 8 float型 : 0.625000000000000000000000000000 (4 Byte) double型 : 0.625000000000000000000000000000 (8 Byte) long double型: 0.625000000000000000000000000000 (16 Byte) 有理数 1 / 10 float型 : 0.100000001490116119384765625000 (4 Byte) double型 : 0.100000000000000005551115123126 (8 Byte) long double型: 0.100000000000000000001355252716 (16 Byte)
0.1 は誤差を含むので下のようなプログラムを書くと無限ループとなり停止しない.
for (double x = 0.0; x != 1.0; x += 0.1) cout << x << endl;
小数が誤差を含むかの判定方法
10進数の小数 0.625 などが2進法で有限小数とはならずに誤差を含むかどうかの簡潔な判定方法を考える.
小数部が で 桁の小数は分数で と書けて となる.この分数が2進法で有限小数となるためには分母が2の冪乗となるので, が で割り切れる必要がある.したがって,判定方法は次の通りである.
追記.float型のビット表現
浮動小数点数型のビット表現を取得するには 共用体 を使用する.浮動小数点数型と誤差に関しては 浮動小数点数型と誤差 を参照.
有理数 をfloat型で表現すると となった.
ある型のオブジェクトが格納されているバイト列を別の型のオブジェクトと解釈してアクセスする方法を type punning と呼びます.type punning は C 言語では認められていますが C++ 17 以前では未定義の動作になります.C++ 20 からは std::bit_cast を使用すことによって合法的かつ安全に行えます.
追記(2023年10月8日)
C++17 から 十六進浮動小数点数リテラル が導入されました。この機能は浮動小数点数リテラルの仮数部を十六進数で表記できるので、上で見てきた十進数から二進数の変換で起こる誤差を生じさせずに記述することができます。
用語の定義が曖昧なので後で書き直すはず.たぶんみんな知っていて書くことでもないのかなと思いながらもとりあえず書いた.曖昧なまま書いているので指摘してもらえると嬉しいです.
ソースコード上で実数は有限小数でしか書けないけど(四則演算すると循環小数になるものとかあるけど),そのような数でも計算機内部で浮動小数点数として扱おうとすると誤差を含む場合が多いという気持ちが分かるはず(∵ 分母が2の冪乗).0.1255 を見て「 1255 が で割り切れないから誤差含むよ」と言われたところであまり嬉しくはないかもしれないけど,誤差がいろいろなところで発生するんだなという気持ちが分かるはず(なぜ書いたかのモチベーションが分からなくなってきた).言いたいことは数値計算ってスゴイなということです.