2.4 数値型の範囲と誤差

(1/1)
金髪ツインテールの女性が数値型の範囲と誤差を学ぶ
数値型の範囲と誤差を学ぶ
AI生成コンテンツ / AI-generated content
JavaScript のNumber型は、およそ±1.8×10308までの有限値を表せる。0に最も近い正の値は約4.94×10-324である。ただし、2進数の有限桁で値を表すため、多くの10進小数は近似値となり、簡単な計算でも丸め誤差が生じる。また、大きすぎる計算結果は Infinity になる場合がある。
また、コラム欄では、任意精度の整数を扱う BigInt型 を使って、巨大な素数候補を生成・判定するプログラムを紹介する。BigIntは整数専用であり、小数は扱えない。

目次

サンプル・プログラム

大きな整数

前回、プログラム電卓用に開発されたインテル 4004 は4ビットCPUだったと書いた。4ビットの並びは $2^4=16$ 通りあり、0~15の符号なし整数を表せる。ただし、CPUのビット数だけで計算できる数の範囲が決まるわけではなく、複数の桁に分けて計算する命令やプログラムも使える。
1972年(昭和47年)、インテルは8ビットCPU 8008 を発表した。その後、汎用レジスタやアドレスの基本幅は16ビット、32ビット、64ビットへ広がった。64ビットの並びは $2^{64}=18,446,744,073,709,551,616$ 通りあるが、符号の有無、命令、データ型によって表せる範囲は異なる。CPUのビット数とJavaScriptのNumber型の精度は、同じ意味ではない。

では、プログラムで扱える整数の大きさは、CPUのビット数に左右されるのかというと、そうであるときと、そうでないときがある。
JavaScriptの場合、Number.MAX_SAFE_INTEGER は、Number型で正確に区別できる「安全な整数」の最大値を表す。処理系ごとに変わる値でも、Number型で表せる最大の整数でもない。

minmax1.html

  19: <script>
  20: //ページのロード時に実行
  21: window.onload = function() {
  22:     document.getElementById('var1').innerHTML = Number.MAX_SAFE_INTEGER;
  23: };
  24: </script>

"minmax1.html" は Number.MAX_SAFE_INTEGER を表示するプログラムだ。この定数を実装した環境では、ECMAScript仕様で定められた 9007199254740991($2^{53}-1$)になる。64ビットCPUの符号なし整数の最大値とは異なる。

浮動小数点数

これまで整数の話ばかりしてきたが、数値計算では小数も扱わなければならない。JavaScriptの Number型は、整数と小数を区別せず、基本的に同じ数値型として扱う。
電気・電子分野の標準化団体 IEEE (アイ・トリプル・イー)  が定めた IEEE 754 規格には、コンピュータで浮動小数点数を扱う方式が規定されている。JavaScriptのNumber型は、そのbinary64(倍精度)に対応する。
浮動小数点数
浮動小数点数
binary64は符号1ビット・指数11ビット・小数部52ビット。正規化数の有効桁は53ビット
IEEE 754 binary64の構成(枠の幅はビット数に比例しない)
IEEE 754のbinary64 は、符号部1ビット、指数部11ビット、仮数を表す小数部52ビットの合計64ビットで構成される。正規化された有限値では先頭の1が省略されるため、有効桁は53ビット分ある。たとえば10進数で1234を1.234×103と書くように、数の大きさを指数で調整する。2進数でも、このように小数点の位置を動かして表す数を浮動小数点数と呼ぶ。

JavaScriptで安全に扱える整数の最大値 Number.MAX_SAFE_INTEGER は $2^{53}-1=9,007,199,254,740,991$ である。これを超える整数もNumber型で表せるが、隣り合う整数を区別できなくなる場合がある。
安全な整数の最小値は Number.MIN_SAFE_INTEGER で定義され、$-(2^{53}-1)=-9,007,199,254,740,991$ となる。

「正規化数」は有効数字の先頭を1にそろえた数であり、「非正規化数」は0に近い領域で精度を減らして表す数である。
Number型で表せる有限値の最大絶対値は約 1.7976931348623157×10308 である。最小の正の正規化数は約2.225×10-308だが、非正規化数を含めると0より大きい最小値は Number.MIN_VALUE、約4.94×10-324である。

整数の計算誤差

"minmax2.html" は Number.MAX_SAFE_INTEGER と、それに1を加算した結果、8を加算した結果を表示するプログラムだ。

minmax2.html

  19: <script>
  20: //ページのロード時に実行
  21: window.onload = function() {
  22:     let a = Number.MAX_SAFE_INTEGER;
  23:     document.getElementById('let1').innerHTML = 'a = ' + a.toLocaleString() + '...' + typeof a;
  24: 
  25:     //Numberの安全な整数の最大値+1
  26:     let b = a + 1;
  27:     document.getElementById('let2').innerHTML = 'b = ' + b.toLocaleString() + '...' + typeof b;
  28: 
  29:     //Numberの安全な整数の最大値+8
  30:     let c = a + 8;
  31:     document.getElementById('let3').innerHTML = 'c = ' + c.toLocaleString() + '...' + typeof c;
  32: 
  33:     //BigIntがあれば
  34:     if (typeof BigInt != 'undefined') {
  35:         let d = BigInt(a) + BigInt(8);
  36:         document.getElementById('let4').innerHTML = 'd = ' + d.toLocaleString() + '...' + typeof d;
  37:     }
  38: };
  39: </script>

1を加えた9,007,199,254,740,992は正確に表せるが、その先では隣り合う整数をすべて表せなくなる。8を加えた正しい答えは9,007,199,254,740,999だが、Number型では9,007,199,254,741,000になる。ちょうど中間の場合は、2進数の有効桁の末尾が0になる方へ丸める。
BigInt型を使えば、このような誤差を起こすことなく計算できる。

JavaScriptは、安全な整数の範囲を外れても自動的にはエラーを出さない。正確な整数が必要なら、入力値・途中の値・結果を Number.isSafeInteger で確認するか BigInt型 を使う。小数では、用途に応じて許容誤差、丸め規則、有効桁を決め、金額など10進数での正確さが必要な処理では整数化や10進演算ライブラリを検討する。

古いブラウザとの違い

Internet Explorer 11には Number.MAX_SAFE_INTEGER とBigIntが実装されていない。"minmax3.html" は、MAX_SAFE_INTEGERがない場合に $2^{53}-1$ を計算する歴史的な互換例である。IE11デスクトップアプリは一部のWindows 10で2022年(令和4年)6月15日にサポートを終了しており、新しいプログラムでは標準的な現行ブラウザを対象にする。

minmax3.html

  19: <script>
  20: //ページのロード時に実行
  21: window.onload = function() {
  22:     console.log(Number.MAX_SAFE_INTEGER);
  23:     let a;
  24:     if (typeof Number.MAX_SAFE_INTEGER != 'undefined') {
  25:         a = Number.MAX_SAFE_INTEGER;
  26:     } else {
  27:         a = Math.pow(2, 53) - 1;
  28:     }
  29:     document.getElementById('let1').innerHTML = 'a = ' + a + '...' + typeof a;
  30: 
  31:     //Numberの安全な整数の最大値+1
  32:     let b = a + 1;
  33:     document.getElementById('let2').innerHTML = 'b = ' + b + '...' + typeof b;
  34: 
  35:     //Numberの安全な整数の最大値+8
  36:     let c = a + 8;
  37:     document.getElementById('let3').innerHTML = 'c = ' + c + '...' + typeof c;
  38: 
  39:     //BigIntがあれば
  40:     if (typeof BigInt != 'undefined') {
  41:         let d = BigInt(a) + BigInt(8);
  42:         document.getElementById('let4').innerHTML = 'd = ' + d + '...' + typeof d;
  43:     }
  44: };
  45: </script>

IE11でもNumber型の計算自体はIEEE 754の倍精度浮動小数点数に基づく。

2021年(令和3年)当時の実行結果を参考として掲げる。IE11ではBigIntの行が表示されないが、Number型の3行は同じ結果である。
●IE11(Windows 10 64bit)
a = 9007199254740991...number
b = 9007199254740992...number
c = 9007199254741000...number
●Edge 91.0.864.64(Windows 10 64bit)
a = 9007199254740991...number
b = 9007199254740992...number
c = 9007199254741000...number
d = 9007199254740999...bigint
●Chrome 91.0.4472.114(Windows 10 64bit)
a = 9007199254740991...number
b = 9007199254740992...number
c = 9007199254741000...number
d = 9007199254740999...bigint
●Chrome 91.0.4472.120(Android)
a = 9007199254740991...number
b = 9007199254740992...number
c = 9007199254741000...number
d = 9007199254740999...bigint
●Firefox 89.0.2(Windows 10 64bit)
a = 9007199254740991...number
b = 9007199254740992...number
c = 9007199254741000...number
d = 9007199254740999...bigint
●Safari(iOS 14.6)
a = 9007199254740991...number
b = 9007199254740992...number
c = 9007199254741000...number
d = 9007199254740999...bigint

小数の計算誤差

"calcError1.html" は $ 6 - 5.4 $ という簡単な小数の減算結果を表示するものだ。

calcError1.html

  19: <script>
  20: //ページのロード時に実行
  21: window.onload = function() {
  22:     let a = 6;
  23:     document.getElementById('let1').innerHTML = 'a = ' + a.toString() + ' ... ' + typeof a;
  24: 
  25:     let b = 5.4;
  26:     document.getElementById('let2').innerHTML = 'b = ' + b.toString() + ' ... ' + typeof b;
  27: 
  28:     let c = a - b;
  29:     document.getElementById('let3').innerHTML = 'a - b = ' + c.toString() + ' ... ' + typeof c;
  30: };
  31: </script>

実行すると、次のように0.6からわずかにずれた結果になる。
a = 6 ... number
b = 5.4 ... number
a - b = 0.5999999999999996 ... number
小数第1位までの値を使っただけなのに、なぜ誤差が出るのか。これは、5.4を2進数の有限桁で正確に表せず、保存する時点で近似値に丸めるためである。

1.0未満の小数部は、分母が2のべき乗となる分数、すなわち $ \displaystyle \frac{1}{2} , \frac{1}{4} , \frac{1}{8} $...の組み合わせで表現しなければならない。
ところが0.4は、2進数では0.011001100110…と同じ並びが続く循環小数である。分数で書くと $ 0.4 = 2^{-2} + 2^{-3} + 2^{-6} + 2^{-7} + \cdots $ となり、有限個の項では表せない。
この近似値を使うため、簡単な減算でも誤差が現れる。

calcError2.html

  19: <script>
  20: //ページのロード時に実行
  21: window.onload = function() {
  22:     const mag = 10;     //倍率
  23: 
  24:     let a = 6;
  25:     document.getElementById('let1').innerHTML = 'a = ' + a.toString() + ' ... ' + typeof a;
  26: 
  27:     let b = 5.4;
  28:     document.getElementById('let2').innerHTML = 'b = ' + b.toString() + ' ... ' + typeof b;
  29: 
  30:     let c = (a * mag - b * mag) / mag;
  31:     document.getElementById('let3').innerHTML = 'a - b = ' + c.toString() + ' ... ' + typeof c;
  32: };
  33: </script>

そこで、倍率定数 mag を設け、計算前に10倍して整数として減算し、最後に mag で除算すると、この例では0.6と表示される。ただし、0.6自体もNumber型では近似値であり、どの小数でも正確になる方法ではない。

calcError3.html

  19: <script>
  20: //ページのロード時に実行
  21: window.onload = function() {
  22:     const mag = 100;        //倍率
  23: 
  24:     let a = 20.42;
  25:     document.getElementById('let1').innerHTML = 'a = ' + a.toString() + ' ... ' + typeof a;
  26: 
  27:     let b = 10.42;
  28:     document.getElementById('let2').innerHTML = 'b = ' + b.toString() + ' ... ' + typeof b;
  29: 
  30:     let c = (a * mag - b * mag) / mag;
  31:     document.getElementById('let3').innerHTML = 'a - b = ' + c.toString() + ' ... ' + typeof c;
  32: };
  33: </script>

ところが、$ 20.42 - 10.42 $ を同じ方法で計算すると10.000000000000002になる。20.42は保存時から近似値であり、100倍した結果も2042ではなく2042.0000000000002となるためである。「100倍すれば必ず整数になる」とは限らない。

金額など小数桁数が決まっている値は、入力を文字列として読み、最小単位の整数へ変換して計算する方法がある。測定値では有効数字と丸め規則を決め、比較するときは許容誤差を考慮する。一般的な10進演算が必要なら、検証された10進演算ライブラリを使う方が安全である。

同じIEEE 754の倍精度を使うPHP、Python、C++などでも、内部では同種の表現誤差が起こり得る。画面表示の桁数や丸め方によって、誤差が見えない場合がある。詳しくは「有限小数、循環小数、分数、無理数」をご覧いただきたい。

オーバーフロー

JavaScriptのNumber型で扱える最大の有限値は Number.MAX_VALUE という定数で参照できる。

minmax4.html

  19: <script>
  20: //ページのロード時に実行
  21: window.onload = function() {
  22:     const a = Number.MAX_VALUE;
  23:     document.getElementById('let1').innerHTML = 'a = ' + a + '...' + typeof a;
  24: 
  25:     //Numberの最大有限値×2
  26:     let b = a * 2;
  27:     document.getElementById('let2').innerHTML = 'b = ' + b + '...' + typeof b;
  28: 
  29:     //BigIntで2の1024乗を正確に計算
  30:     if (typeof BigInt != 'undefined') {
  31:         const c = 2n ** 1024n;
  32:         document.getElementById('let3').innerHTML = 'c = ' + c + '...' + typeof c;
  33:     }
  34: };
  35: </script>

"minmax4.html" は Number.MAX_VALUE を2倍するものだ。
計算オーバーフローが起き、変数bは文字列ではなくNumber型の特殊値 Infinity になる。
BigInt型 では $2^{1024}$ を正確な整数として計算できるが、Number型とBigInt型は用途が異なり、演算前に型をそろえる必要がある。
a = 1.7976931348623157e+308...number
b = Infinity...number
c = 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216...bigint
有限の計算結果かどうかは Number.isFinite で確認できる。ただし、最大値を少しでも超える数学上の結果が必ずInfinityになるわけではない。たとえばNumber.MAX_VALUE + 1は、丸められてNumber.MAX_VALUEのままになる。

小さすぎる値とNaNの確認

小さすぎる値では精度が失われ、0に丸められることもある。これをアンダーフローという。たとえばNumber.MIN_VALUE / 2は0となる。また、0 / 0やNumber("abc")は、数値として有効な結果を得られなかったことを表す NaN になる。
Number.isFinite(Infinity); // false
Number.isFinite(NaN); // false
Number.isFinite(0.6); // true
Number.isNaN(0 / 0); // true
Number.isFiniteは有限かどうかを調べるもので、誤差の有無は判定しない。NaNはNaN自身とも等しくならないため、NaN === NaNではなくNumber.isNaNを使う。

コラム:分数計算

金髪ツインテールの女性が夜に分数を勉強する
分数を使った正確な計算を学ぶ
AI生成コンテンツ / AI-generated content
分子と分母を整数として保持する分数計算なら、有理数を正確に表せる。ただし、分母が0の値や、円周率のような無理数は別に扱う必要がある。JavaScriptの標準組み込み型には分数型がないため、ライブラリを使うか、BigIntで分子と分母を管理するクラスなどを用意する。
Pythonには標準ライブラリの fractions があり、Fraction型で有理数を計算できる。10進小数を正確に扱いたい場合は、言語やライブラリが提供する10進型も選択肢になる。
小学校の算数で、分数の計算でつまずく子どもが多いと聞くが、プログラミングでは分数の考え方が必要になる場面がある。前回のコラムで、プログラミングを学ぶ前に算数の基礎力を付けた方がいいと書いたが、分数についても同じことが言える。

コラム:BigInt型

巨大素数サンプルの2023年版の画面例
2023年版の画面例(現在の結果表示とは異なる)
JavaScript の BigInt型 は、巨大な整数のほか、連番や時刻などNumber型の安全な整数範囲を超える値にも使える。ここではBigIntの例として、巨大な素数候補を生成・判定するプログラムを紹介する。素数は暗号技術でも使われるが、このサンプルは学習用であり、暗号鍵の生成には使用しない。
BigInt型 の整数リテラル(コードに直接書く値)は、整数の末尾に $ n $ を付記し、 "1234567890n" のように表記する。
BigInt型 では多くの算術演算子、ビット演算子、比較演算子を使える。ただし、Number型と混ぜた算術演算はできず、Mathオブジェクトの多くの関数もBigIntを受け付けない。乱数、べき剰余(累乗した数を割った余り)、素数判定はユーザー関数として用意する。

大きな整数は最初から12345678901234567890n、またはBigInt("12345678901234567890")のように書く。Number型で丸められた値を後からBigIntへ変換しても、失われた桁は戻らない。逆にBigIntをNumberへ変換すると精度を失う場合がある。
また、5n / 2nは2nであり、小数部分は0に近い方向へ切り捨てる。0nでの除算はInfinityではなく例外となる。

サンプルの素数とは、2以上で、1と自分自身以外の正の整数では割り切れない整数のことである。2、3、5、7などが該当し、1は素数ではない。このサンプルの実行には、BigIntとcrypto.getRandomValuesに対応したブラウザが必要である。

bigPrime.html

  96: /**
  97:  * BigIntの乱数を求める.
  98:  * @param   BigInt min 最小値
  99:  * @param   BigInt max 最大値
 100:  * @return  BigInt min以上max以下の乱数
 101:  * @throws  RangeError minがmaxより大きい
 102: */
 103: function BigIntRandom(min, max) {
 104:     min = BigInt(min);
 105:     max = BigInt(max);
 106:     if (min > max) {
 107:         throw new RangeError('minはmax以下にしてください');
 108:     }
 109: 
 110:     const range = max - min + 1n;
 111:     if (range === 1n) return min;
 112:     const bitLength = (range - 1n).toString(2).length;
 113:     const byteLength = Math.ceil(bitLength / 8);
 114:     const excessBits = byteLength * 8 - bitLength;
 115:     const bytes = new Uint8Array(byteLength);
 116:     let value;
 117:     do {
 118:         crypto.getRandomValues(bytes);
 119:         if (excessBits > 0) {
 120:             bytes[0] &= 0xff >>> excessBits;
 121:         }
 122:         value = 0n;
 123:         for (const byte of bytes) {
 124:             value = (value << 8n) | BigInt(byte);
 125:         }
 126:     } while (value >= range);
 127: 
 128:     return min + value;
 129: }

ユーザー関数 BigIntRandom は、指定範囲のBigInt乱数を生成する。Math.random は暗号学的に安全ではなく、乱数の範囲への変換方法によっては偏りも生じるため、このサンプルでは crypto.getRandomValues でランダムなバイト列を作り、範囲外の値を捨てる方法を用いる。

bigPrime.html

 170: /**
 171:  * ミラー・ラビン素数判定法により引数(BigInt)が素数かどうかを判定する.
 172:  * @param   BigInt n 判定する整数
 173:  * @param   Number rounds 試行回数(正の安全な整数)
 174:  * @return  boolean true:おそらく素数/false:素数ではない
 175: */
 176: function MillerRabinPrimalityTest(n, rounds = 20) {
 177:     if (typeof n !== 'bigint') throw new TypeError('BigIntを指定してください');
 178:     if (!Number.isSafeInteger(rounds) || rounds < 1) throw new RangeError('試行回数は正の安全な整数にしてください');
 179:     if (n < 2n) {
 180:         return false;
 181:     }
 182:     if ((n == 2n) || (n == 3n)) {
 183:         return true;
 184:     }
 185:     if ((n & 1n) == 0n) {
 186:         return false;
 187:     }
 188: 
 189:     let d = n - 1n;
 190:     let s = 0;
 191:     while ((d & 1n) == 0n) {
 192:         d >>= 1n;
 193:         s++;
 194:     }
 195: 
 196:     for (let i = 0; i < rounds; i++) {
 197:         const a = BigIntRandom(2n, n - 2n);
 198:         let x = BigIntModpow(a, d, n);
 199:         if ((x == 1n) || (x == n - 1n)) {
 200:             continue;
 201:         }
 202: 
 203:         let witness = true;
 204:         for (let r = 1; r < s; r++) {
 205:             x = (x * x) % n;
 206:             if (x == n - 1n) {
 207:                 witness = false;
 208:                 break;
 209:             }
 210:         }
 211:         if (witness) {
 212:             return false;
 213:         }
 214:     }
 215:     return true;
 216: }

ユーザー関数 MillerRabinPrimalityTest は、与えた BigInt型 の整数が素数かどうかを確率的に判定する。
素数判定には、「PHPとPythonで巨大素数を扱う」で紹介したミラー・ラビン素数判定を用いている。
判定には、べき剰余関数 BigIntModpow を使う。補助関数 BigIntLog も掲載するが、現在の判定処理からは呼び出していない。BigIntLogは対数の小数部分を切り捨てた整数を返す関数で、たとえばBigIntLog(999n, 10n)は2nとなる。

bigPrime.html

 131: /**
 132:  * BigIntの対数を求める.
 133:  * @param   BigInt n    整数
 134:  * @param   BigInt base 基数
 135:  * @return  BigInt 対数の整数部分(切り捨て)
 136: */
 137: function BigIntLog(n, base) {
 138:     if (typeof n !== 'bigint' || typeof base !== 'bigint') throw new TypeError('BigIntを指定してください');
 139:     if (n < 1n || base < 2n) throw new RangeError('nは1以上、baseは2以上にしてください');
 140:     let l = 0n;
 141:     let x = n;
 142:     while ((x = x / base) > 0n) {
 143:         l++;
 144:     }
 145:     return l;
 146: }

bigPrime.html

 148: /**
 149:  * BigIntのべき剰余を求める.
 150:  * @param   BigInt base 基数
 151:  * @param   BigInt exp  指数
 152:  * @param   BigInt mod  除数
 153:  * @return  BigInt べき剰余
 154: */
 155: function BigIntModpow(base, exp, mod) {
 156:     if (typeof base !== 'bigint' || typeof exp !== 'bigint' || typeof mod !== 'bigint') throw new TypeError('BigIntを指定してください');
 157:     if (exp < 0n || mod <= 0n) throw new RangeError('指数は0以上、除数は1以上にしてください');
 158:     base = ((base % mod) + mod) % mod;
 159:     let res = 1n % mod;
 160:     while (exp > 0n) {
 161:         if ((exp & 1n) == 1n) {
 162:             res = (res * base) % mod;
 163:         }
 164:         base = (base * base) % mod;
 165:         exp >>= 1n;
 166:     }
 167:     return res;
 168: }

bigPrime.html

  32: const MAX_DIGITS  = 200;        //判定できる整数の最大桁数

定数 MAX_DIGITS に、生成・判定できる整数の最大桁数を設定する。生成時は1~200の中から桁数を選び、その桁数の素数候補を探すため、毎回200桁になるわけではない。
ECMAScript 仕様はBigIntの固定最大値を定めていないが、実際の上限はメモリや処理系に左右される。ミラー・ラビン素数判定は確率的判定なので、結果は「おそらく素数」であり、試行回数を増やすほど合成数を素数と誤判定する確率を下げられる。桁数が増えるほど生成・判定時間も長くなるため、このサンプルでは最大200桁に制限している。処理中は画面操作が一時的に止まることがある。

コラム:表示と内部の値は同じとは限らない

金髪ツインテールの女性が円周率の丸めを講義する
円周率を小数第3位までに丸める
AI生成コンテンツ / AI-generated content
本文のサンプル・プログラム "calcError1.html" では、演算結果が0.599999‥‥と表示された。同じIEEE 754の値でも、プログラミング言語、処理系、出力関数、書式指定によって画面に出す桁数が異なるため、0.6と表示される場合もある。
短く表示されたからといって、内部の表現誤差が消えたわけではない。出力時は用途に合った有効数字と丸め方を明示し、計算時の精度とは分けて考える必要がある。
まず、JavaScriptだが、下記のスクリプトを実行してみてほしい。
let a = 0.599999999999999;
console.log(a);
通常は0.599999999999999と表示される。Number型の標準的な文字列化では、読み戻したときに同じ内部の値となる、できるだけ短い10進表記を使う。console.logの表示方法そのものは実行環境にも依存する。

次は、Pythonだ。
a = 0.599999999999999
print(a);
print関数 でも通常は0.599999999999999と表示される。ただし、Pythonのfloatにも2進浮動小数点数の表現誤差はある。

それでは、PHPはどうか――
$a = 0.599999999999999;
print($a);
出力結果はPHPのバージョンや precision 設定に左右される。0.6と表示されても、内部計算が10進数で正確になったことを意味しない。

最後に、C++はどうだろう。
double a = 0.599999999999999;
std::cout << a << std::endl;
既定の精度では0.6と表示されることがある。必要な桁数は std::setprecision などで指定する。C++のdoubleにも同じ種類の表現誤差があり、表示だけで精度を判断してはいけない。

参考サイト

(この項おわり)
header