著者|プログラミングについて話す
ソース|ランダムトークプログラミング(ID:mhcoding)
コンピューターブラウザを使用して0.2+ 0.1を計算すると、解は0.30000000000000004ですが、0.1 +0.6の結果は0.7になるのはなぜですか。
この問題は常に古典的な問題でした。ドメイン名がhttps://0.30000000000000004.com/であるWebサイトもあり、主にこの問題を説明しています。
このウェブサイトでは、さまざまなプログラミング言語で0.2 + 0.1を計算した結果が一覧表示され、いくつかは次のように選択されています:

さまざまな言語で、0.2 + 0.1の計算結果が驚くほど一貫していることがわかります。これは、この魔法の0.30000000000000004です。
実際、ブラウザのコンソール(F12)を使用して計算を実行する場合、JavaScript言語を使用して計算を実行します。したがって、上記の現象は、最終的な分析の特定のプログラミング言語とは関係ありません。
主な問題は、コンピューターで小数を表現する方法と、小数演算を実行する方法です。
コンピューターは0と1のみを認識することがわかっています[コンピューターが0と1のみを認識する理由]。コンピューターに保存、計算、または表示する場合は、実世界のコンテンツをバイナリシステムに変換する必要があります。現実の世界では、数字には主に整数と小数が含まれます。
前回の記事[コンピューターがデータを格納するために補数を使用する理由]では、元のコード、逆コード、補数など、コンピューターで整数を表す方法が多数あることを紹介しました。
整数には、正の整数、負の整数、およびゼロが含まれます。コンピュータに保存されている整数は、符号付きの数値と符号なしの数値に分けられます。
符号なしの場合は、どのエンコード方法を使用するかは関係ありません。符号付きの場合は、一般的に使用されるのは補数です。
次に、10進数でバイナリ補数を取得する場合は、特定のアルゴリズムを使用して、対応する元のコードを取得する必要があります。
10から2
まず、10進整数を2進整数に変換する方法を見てみましょう。
10進数から2進数への変換は、「2で割り、残りを取り、逆の順序で並べる」方法を採用しています。
具体的なアプローチは次のとおりです。
10進整数を2で割って、商と残差を求めます。
2を使用して商を再度削除すると、商が1未満になるまで、商と残りが取得されます。
次に、最初に取得した残りをバイナリ番号の下位ビットとして、後で取得した残りをバイナリ番号の上位ビットとして取得し、順番に並べます。
たとえば、次のように127をバイナリに変換します。
では、10進数から2進数への変換をどのように計算しますか?
10進数から2進数への変換は、「2を掛けて順番に丸める」方法を採用しています。
具体的な方法は次のとおりです。*小数部分に2を掛けて積を求めます*積の整数部分を取り出し、残りの分数部分に2を掛けて別の積を求めます*次に、積の整数部分を取り出し、積が入るまで行います。の小数部はゼロです。この時点で、0または1はバイナリの最後の桁です。または、必要な精度に達するまで。
したがって、10進数の0.625のバイナリ値は0.101です。
すべての数値をバイナリで表すことができるわけではありません
10進数を2進数に変換する方法を知っているので、2進数を使用して10進数を直接表すことはできますか?
前の例では、0.625は特別な列なので、同じアルゴリズムを使用して、0.1に対応するバイナリを計算してください。
0.1のバイナリ表現には無限ループがあることがわかりました。つまり、(0.1)10 =(0.000110011001100 ...)2です。
この場合、コンピューターは0.1をバイナリで正確に表すことができません。
つまり、0.1のような数値の場合、特定の2進数に変換することはできません。
IEEE 754
一部の小数部をバイナリで正確に表現できないという問題を解決するために、IEEE754仕様があります。
バイナリ浮動小数点演算のIEEE標準(IEEE 754)は、1980年代以来最も広く使用されている浮動小数点演算規格であり、多くのCPUおよび浮動小数点演算ユニットで採用されています。
浮動小数点数と小数は完全に同じではありません。実際には、コンピューターには固定小数点と浮動小数点の2種類の10進表現があります。同じ桁数の場合、固定点数の表現範囲は浮動小数点数の表現範囲よりも狭いためです。そのため、コンピュータサイエンスでは、浮動小数点数を使用して実数の概算値を表します。
IEEE 754は、浮動小数点値を表す4つの方法を指定しています。単一精度(32ビット)、二重精度(64ビット)、拡張単一精度(43ビットを超える、ほとんど使用されない)、および拡張二重精度(79ビット)です。上記は通常80ビットで実装されます)。
最も一般的に使用されるのは、32ビットの単精度浮動小数点数と64ビットの倍精度浮動小数点数です。
IEEEは、小数を正確に表すことができないという問題を解決しませんでしたが、小数を表すために近似値を使用する方法を提案し、精度の概念を導入しただけです。
浮動小数点数は、次の図に示すように、0と1の文字列で構成されるビットシーケンスであり、論理的にトリプル{S、E、M}を使用して数Nを表します。
S(符号)はNの符号ビットを表します。対応する値sは、n> 0の場合、s = 0、n≤0の場合、s = 1を満たします。
E(指数)は、SとMの間にあるNの指数ビットを表します。対応する値eは、正または負にすることもできます。
M(マンティッサ)はNのマンティッサを表し、たまたまNの終わりにあります。Mは、有意、係数、または「10進」とも呼ばれます。
次に、浮動小数点数Nの実際の値nは、次の式で表されます。
上記の式は非常に複雑に見えます。符号ビットとマンティッサビットは比較的理解しやすいですが、指数ビットはそれほど理解しやすいものではありません。
実際、この式にあまり絡む必要はありません。単一精度の浮動小数点数の場合、数値を表すために使用できるのは32ビット文字のみであり、倍精度の浮動小数点数は数値を表すために64ビットしか使用できないことを知っておく必要があります。
ループが無限大のバイナリ番号の場合、コンピュータは浮動小数点数を使用して特定の数の有意な数値を保持するため、この値は概算値であり、真の値ではありません。
数値に対応するIEEE754浮動小数点数の計算方法については、この記事の焦点ではありません。ここでは繰り返しません。プロセスはまだ比較的複雑です。順序、マンティッサの合計、正規化、丸め、およびオーバーフローの判断を実行する必要があります。
しかし、これらのことを詳細に理解する必要はありません。コンピュータでの小数の表現は概数であり、真の値ではないことを知っておく必要があります。精度によって、近似度も異なります。
たとえば、10進数の0.1、倍精度浮動小数点数に対応するバイナリは、0.000110011001100110011001100110011001100110011001100110011001です。
0.2の小数は0.001100110011001100110011001100110011001100110011001100110011です。
したがって、2つを足し合わせます。
10進数に変換すると、0.30000000000000004になります。
精度の低下を回避する
Javaでは、floatは単一精度の浮動小数点数を表すために使用され、doubleは概算値である倍精度の浮動小数点数を表すために使用されます。
したがって、Javaコードでは、floatまたはdoubleを使用して高精度の計算、特に金額の計算を実行しないでください。そうしないと、資産が失われやすくなります。
この精度の問題を解決するために、正確な計算のためにBigDecimalがJavaで提供されています。
参考資料:
https://0.30000000000000004.com/
https://zh.wikipedia.org/zh-hans/IEEE_754
https://www.h-schmidt.net/FloatConverter/IEEE754.html
更多精彩推荐
☞服!AI 让兵马俑“活”起来,颜值惊艳!
☞华为 Mate 40/Pro 系列全球发布会日期定了..... | 每日趣闻
☞对话阿里云:开源与自研如何共处?
☞AI 还原康乾盛世三代皇帝的样貌,简直太太太好玩了!
☞如何应对云原生之旅中的安全挑战?
☞观点 | 回顾以太坊近期及中期扩容路线图,展望 rollup 作为中心的以太坊路线图
点分享点点赞点在看