Javaで10/3.0はなぜ3.3333333333333335と表示されるのかがわかりません。
末尾の5が3にならなければならないと考えます。
コードを示します。
Java
1double d = 10/3.0; 2System.out.println( d + " = " + Long.toBinaryString(Double.doubleToRawLongBits(d)));
表示は以下のようになります。
3.3333333333333335 = 100000000001010101010101010101010101010101010101010101010101011
IEEE 754の浮動小数点フォーマットの仮数部の末尾が1です。
これが原因ではないのかと思います。次の情報を知るべきかなと思います。
- IEEE 754で仮数部を正規化する方法は
- Intelプロセッサーの浮動小数点の扱いは
- Java内部でどのように浮動小数点を処理するのか
1),2),3)についてご説明いただくか、解説サイトを紹介していただけないでしょうか。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答9件
0
そもそも、IEEE 754のdouble
(Javaはそれで固定)は仮数部が(ケチった1ビットも足して)53ビット相当なので、精度は2進法で53桁、10進法で数えると53 * log10(2) ≒ 16桁弱しかありません。
そして、100000000001010101010101010101010101010101010101010101010101010
を10進法に直すと、「3.333333333333333」(1桁少ないので、「3.3333333333333330」と同じ)になります。3.33333…はこの値より、3.3333333333333335のほうが近いので、そちらに丸められています。
投稿2018/04/20 00:19
総合スコア146117
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/04/20 01:18
0
メタなコメントで失礼します。
みなさん丁寧に解説しておられますが・・・
「IEEE 754」という仕様の名前をご存知なので、検索して
https://ja.wikipedia.org/wiki/IEEE_754
といった詳細な説明のページは簡単に見つけられたのではないかと思います。しかしそれでも質問にいたったのは何か課題があったからだと思います。それをもっと詳しく書いた方がよかったんじゃないでしょうか。この解説ページを読むのがめんどかったのか、数値表現の意味が今一つピンときていないのか・・・などなど
あなたの課題点を閲覧者にうまく伝えないとせっかく回答がついてもそれが「あなたにとって本当に必要で参考になる役立つアドバイス」なのかはわからないのではないでしょうか?
つまり自分も質問コメントされたお二人と同じような印象を持ちました。
投稿2018/04/20 02:49
総合スコア18402
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
仕様を貼って欲しそうに見えたので。
Java Language Specification
4.2.4. Floating-Point Operations
The Java programming language requires that floating-point arithmetic behave as if every floating-point operator rounded its floating-point result to the result precision. Inexact results must be rounded to the representable value nearest to the infinitely precise result; if the two nearest representable values are equally near, the one with its least significant bit zero is chosen. This is the IEEE 754 standard's default rounding mode known as round to nearest.
◇グーグル翻訳
Javaプログラミング言語では、すべての浮動小数点演算子が浮動小数点結果を結果精度に丸めたかのように、浮動小数点演算が動作する必要があります。不正確な結果は、無限に正確な結果に最も近い表現可能な値に丸めなければなりません。2つの最も近い表現可能な値が等しく近い場合、最下位ビットがゼロのものが選択される。これは、IEEE 754標準のデフォルト丸めモードで、最も近い丸めと呼ばれます。
投稿2018/04/20 03:27
総合スコア5846
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
浮動小数点の処理云々より、2進数>10進数変換の方を調べるべきなのでは
そもそも、問題は何でしょうか。
計算の結果が100000000001010101010101010101010101010101010101010101010101011 となるのを問題にしているのか、
その表示が3.3333333333333335なのを問題にしてるのか、どっち?
計算結果が間違っていると言うなら計算ライブラリやFPUユニットのバグの話になるだろうし、
表示が問題だと言うなら、浮動小数点から文字列の変換のバグの話になるだろうけど、
どちらにしても、あなたがこだわっている浮動小数点の表現のはなしにはならないと思うんですが
投稿2018/04/19 23:32
編集2018/04/19 23:42総合スコア88055
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
10/3を2進数表記すると、
11.01010101010101...
=1.101010101010101...*2なので、
これを倍精度浮動小数点の仕様に従って表現すると、
- 正の数なので符号部は0
- 指数部は1+1023=1024で、2進法で10000000000
- 仮数部は10の循環で52ビットまで取ると、末尾は10で終わる。(1010...10)
そのあとにまだ1010...と続き、これは10進法で例えるなら丸める桁が51...のように続いていることになり、切り上げが発生する。結果、1010...1011になる
ここからは私の推測だが、
最下位ビットの1には誤差を含むことになる。この1は2進法の小数第51位なので、2^(-51)を表す。
2^(-51) = 0.000000000000000444089209850062616169452667236328125(小数首位が第16位)
であるが、これには最大でこれの半分、
2^(-52) = 0.0000000000000002220446049250313080847263336181640625
のプラスマイナスの誤差を含んでいる。
つまりこの部分だけで考えると、小数首位が2~6の幅を持ち、その下の桁の数字はもはや信頼できる意味を持たない数字ということになる。
そこで、これの小数首位までを有効数字とし(実際この位も振れ幅が大きいが、これを含めないと最下位ビットが0の場合と差がなくなる)、その下の桁で四捨五入して10進数の表記にしていると考えられる。
maisumakunさんのコメントから、完全な数値が
3.333333333333333481363069950020872056484222412109375
ということなので、これの小数17位を四捨五入して
3.3333333333333335
となったのではなかろうか。
投稿2018/04/20 01:42
編集2018/04/20 16:09総合スコア20669
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
※ JavaがIEEE754を採用しているとか、10がdoubleに変換されるとか言ったことの説明は省いている。
※ $...$
の部分はTexでの数式であり、teratailでは変換されない。StackEditとかBoostnoteとか対応している適当なエディタで確認して欲しい。ただ、数式見るのが面倒だという人が多いのであれば、Qiitaに転載しようと思う。
※ 零で割る等の例外処理は除外している。
※ 二進数の小数点数は0b1.1010...
のような形で表現する。0
と1
は二進数であるか十進数であるかは問われないが、それ以外の0bが付かない数は十進数である。
doubleはIEEE754 binary64である。binary64は次の式表される。
$x = (-1)^s \times c \times 2^q$
ただし、s
は0
または1
、c
は1
以上2
未満である。c
は二進表記であり、その小数点数以下の桁は52
である。よて、10.0
と3.0
は次のようになる。
x0 = 10.0 s0 = 0 c0 = 0b1.0100000000000000000000000000000000000000000000000000 q0 = 3
x1 = 3.0 s1 = 0 c1 = 0b1.1000000000000000000000000000000000000000000000000000 q1 = 1
割り算は次のように計算できる。
$$
x_0 \div x_1 = \frac{(-1)^{s_0} \times c_0 \times 2^{q_0}}{(-1)^{s_1} \times c_1 \times 2^{q_1}}
= (-1)^{s_0 - s_1} \times \frac{c_0}{c_1} \times 2^{q_0 - q_1}
$$
まず、
s0 - s1 = 0 q0 - q1 = 2
である、問題は$\frac{c_0}{c_1}$となる。これは0b1.1
の桁を下げながら、商と余りを出していく。
0b1.1
まで計算では、商は0b0
、余りは0b1.01
。0b0.11
まで計算では、商は0b0.1
、余りは0b0.1
。0b0.011
まで計算では、商は0b0.11
、余りは0b0.001
。0b0.0011
まで計算では、商は0b0.110
、余りは0b0.001
。0b0.00011
まで計算では、商は0b0.1101
、余りは0b0.00001
。0b0.000011
まで計算では、商は0b0.11010
、余りは0b0.00001
。0b0.0000011
まで計算では、商は0b0.110101
、余りは0b0.0000001
。0b0.00000011
まで計算では、商は0b0.1101010
、余りは0b0.0000001
。- ...
以下繰り返していくと、1と0の繰り返しになり0b0.11010101010...
と無限に続く事になる。ここで、binary64の形式に合わせると、仮数は1
以上2
未満であるため、一つ位をあげて、その分指数を減らして次のようになる。
s = 0 c = 0b1.1010101010...(無限に10を繰り返す循環小数) q = 1
しかし、c
の小数点数以下は52桁までなので53桁目以降は表現できない。よって53桁目以降を切り上げるか切り捨てるかで52桁目が0
になるか1
になるかが問題になる。これには丸め誤差の仕方によって異なり、IEEE754は5種類用意してある。
- 最近接丸め(偶数): 53桁目以降は1010...と続くため切り上げた方が近いので、52桁目は
1
- 最近接丸め(0から遠い方): 53桁目以降は1010...と続くため切り上げた方が近いので、52桁目は
1
- 0方向への丸め: 正の数なので切り捨てて、52桁目は
0
- +∞への丸め: 切り上げて、52桁目は
1
- -∞への丸め: 切り捨てて、52桁目は
0
これはCPU(に付随するFPU)や命令によって異なるが、Javaでは言語仕様として定められている。
Chapter 4. Types, Values, and Variables
The Java programming language requires that floating-point arithmetic behave as if every floating-point operator rounded its floating-point result to the result precision. Inexact results must be rounded to the representable value nearest to the infinitely precise result; if the two nearest representable values are equally near, the one with its least significant bit zero is chosen. This is the IEEE 754 standard's default rounding mode known as round to nearest.
つまり、1.の最近接丸め(偶数)となり、52桁目は1
である。よって答えはこうなる。
s = 0 c = 0b1.1010101010101010101010101010101010101010101010101011 q = 1
これをビット表現にするのだが、指数部分はバイアスとして$2^{11-1}-1$足す必要があり、また、仮数部分は整数部の1
は省略するためビット表現は次のようになる。
0100000000001010101010101010101010101010101010101010101010101011 sqqqqqqqqqqqcccccccccccccccccccccccccccccccccccccccccccccccccccc
二進数にすれば0b11.010101010101010101010101010101010101010101010101011
である。
次にこれの十進数表記がどのようになっていくかについてだが、(以下省略)
参考文献:
The Java® Language Specification Java 10
Java言語規定 上の翻訳だがバージョンが古いので注意
IEEE 754 - Wikipedia
すいません、十進数表記はこれまた面倒だった記憶があるし、疲れたので、また今度にさせてください。
投稿2018/04/20 13:53
総合スコア21739
0
10/3.0は3.333333333333333の後に3が無限に続きます。
無限桁(無限に続く文字)は表示できないので、どこかの桁で切り上げや切り下げの処理をして有限の桁数にします。
切り上げがされれば、「末尾が3にならなければならない」が成り立たない場合があります。
投稿2018/04/19 23:35
総合スコア6915
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ベストアンサー
皆さま、ありがとうございました。ほとんどの回答は自分が調べたものとおなじでした。質問のピントがずれていることは否めません。改めて質問します。
投稿2018/04/20 04:33
退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/04/20 04:42
2018/04/20 04:45
2018/04/20 04:54
2018/04/20 04:55
2018/04/20 04:58 編集
2018/04/20 05:19
2018/04/20 05:33
2018/04/20 05:36
2018/04/20 08:06
2018/04/20 08:10
2018/04/20 08:15
2018/04/20 08:26
2018/04/20 08:34
2018/04/20 08:40
2018/04/20 08:53
2018/04/20 23:49
0
つ 浮動小数点数(float, double)演算の丸め誤差と対策
[追記]
なぜという質問の答えではないですが^^;
java
1 2package javaapplication1; 3 4import java.math.BigDecimal; 5import java.math.RoundingMode; 6 7 8/** 9 * 10 */ 11public class JavaApplication1 { 12 13 /** 14 * @param args the command line arguments 15 */ 16 public static void main(String[] args) { 17 // TODO code application logic here 18 double d = 10/3.0; 19 System.out.println( d + " = " + Long.toBinaryString(Double.doubleToRawLongBits(d))); 20 // 21 BigDecimal b1 = new BigDecimal("10.0"); 22 BigDecimal b2 = new BigDecimal("3.0"); 23 BigDecimal a = b1.divide(b2,20,RoundingMode.HALF_UP); 24 System.out.println( a ); 25 } 26}
結果:
3.3333333333333335 = 100000000001010101010101010101010101010101010101010101010101011
3.33333333333333333333
投稿2018/04/20 00:23
編集2018/04/20 00:54総合スコア6851
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。