質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

9回答

2688閲覧

Javaで10/3.0はなぜ3.3333333333333335と表示されるのか

退会済みユーザー

退会済みユーザー

総合スコア0

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

1グッド

0クリップ

投稿2018/04/19 23:26

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です。
これが原因ではないのかと思います。次の情報を知るべきかなと思います。

  1. IEEE 754で仮数部を正規化する方法は
  2. Intelプロセッサーの浮動小数点の扱いは
  3. Java内部でどのように浮動小数点を処理するのか

1),2),3)についてご説明いただくか、解説サイトを紹介していただけないでしょうか。

yohhoy👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

a_saitoh

2018/04/19 23:33

1,2,3を調べなくても「2進数で四捨五入したら末尾が11になってそれを10進にしたら5になる」で納得出来ないわけを書いた方が良いように思います。
m.ts10806

2018/04/20 00:15

この質問の大前提と落としどころを明記した方が良いかと。あとJava以外の言語でも調べてみると面白いかもしれません。
fuzzball

2018/04/20 05:23

「仮数部の末尾が1です」なぜ0ではなく1になるのか?という質問でしょうか?
guest

回答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

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2018/04/20 01:18

なお、この2つの値を割り切れるまで正確に出すと、「3.33333333333333303727386009995825588703155517578125」と「3.333333333333333481363069950020872056484222412109375」になるとのことです。
guest

0

メタなコメントで失礼します。

みなさん丁寧に解説しておられますが・・・

「IEEE 754」という仕様の名前をご存知なので、検索して

https://ja.wikipedia.org/wiki/IEEE_754

といった詳細な説明のページは簡単に見つけられたのではないかと思います。しかしそれでも質問にいたったのは何か課題があったからだと思います。それをもっと詳しく書いた方がよかったんじゃないでしょうか。この解説ページを読むのがめんどかったのか、数値表現の意味が今一つピンときていないのか・・・などなど

あなたの課題点を閲覧者にうまく伝えないとせっかく回答がついてもそれが「あなたにとって本当に必要で参考になる役立つアドバイス」なのかはわからないのではないでしょうか?

つまり自分も質問コメントされたお二人と同じような印象を持ちました。

投稿2018/04/20 02:49

KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

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

umyu

総合スコア5846

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

浮動小数点の処理云々より、2進数>10進数変換の方を調べるべきなのでは


そもそも、問題は何でしょうか。
計算の結果が100000000001010101010101010101010101010101010101010101010101011 となるのを問題にしているのか、
その表示が3.3333333333333335なのを問題にしてるのか、どっち?

計算結果が間違っていると言うなら計算ライブラリやFPUユニットのバグの話になるだろうし、
表示が問題だと言うなら、浮動小数点から文字列の変換のバグの話になるだろうけど、

どちらにしても、あなたがこだわっている浮動小数点の表現のはなしにはならないと思うんですが

投稿2018/04/19 23:32

編集2018/04/19 23:42
y_waiwai

総合スコア87774

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

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
swordone

総合スコア20651

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

※ JavaがIEEE754を採用しているとか、10がdoubleに変換されるとか言ったことの説明は省いている。
$...$の部分はTexでの数式であり、teratailでは変換されない。StackEditとかBoostnoteとか対応している適当なエディタで確認して欲しい。ただ、数式見るのが面倒だという人が多いのであれば、Qiitaに転載しようと思う。
※ 零で割る等の例外処理は除外している。
※ 二進数の小数点数は0b1.1010...のような形で表現する。01は二進数であるか十進数であるかは問われないが、それ以外の0bが付かない数は十進数である。


doubleはIEEE754 binary64である。binary64は次の式表される。

$x = (-1)^s \times c \times 2^q$

ただし、s0または1c1以上2未満である。cは二進表記であり、その小数点数以下の桁は52である。よて、10.03.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種類用意してある。

  1. 最近接丸め(偶数): 53桁目以降は1010...と続くため切り上げた方が近いので、52桁目は1
  2. 最近接丸め(0から遠い方): 53桁目以降は1010...と続くため切り上げた方が近いので、52桁目は1
  3. 0方向への丸め: 正の数なので切り捨てて、52桁目は0
  4. +∞への丸め: 切り上げて、52桁目は1
  5. -∞への丸め: 切り捨てて、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

raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

umyu

2018/04/20 14:50

ぜひQiitaとGistにもー。
guest

0

10/3.0は3.333333333333333の後に3が無限に続きます。
無限桁(無限に続く文字)は表示できないので、どこかの桁で切り上げや切り下げの処理をして有限の桁数にします。

切り上げがされれば、「末尾が3にならなければならない」が成り立たない場合があります。

投稿2018/04/19 23:35

coco_bauer

総合スコア6915

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

皆さま、ありがとうございました。ほとんどの回答は自分が調べたものとおなじでした。質問のピントがずれていることは否めません。改めて質問します。

投稿2018/04/20 04:33

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fuzzball

2018/04/20 04:42

質問の修正・追記でいいと思いますが。
Zuishin

2018/04/20 04:45

調べたものと同じ回答しか得られなかったのなら解決しているはずがありませんが、独自に解決されたのならその結果を示してください。
KSwordOfHaste

2018/04/20 04:54

IEEE754の仕様についてそこそこ詳しい情報があるのはご存知のようですのでやはりそれを読み解く前提知識か何かに課題があったのではないでしょうか?本件に限った話ではありませんが、質問の際に自分が何に疑問を感じているのかを整理して詳しく書くというのは大事と思います。例え初歩的な内容でも質問の仕方でよい質問にもそうでないものにもなると思いますよ~
defghi1977

2018/04/20 04:55

これで少なくとも回答者7人×1時間+この記事を目にした人数×10分だけの貴重な時間が浪費されたわけだ…
m.ts10806

2018/04/20 04:58 編集

調べた情報を提示しておかないと回答者みなさんの負担を強いているだけとなります。 本当に知らないのか、調べた結果の正否を問いたいのか はハッキリさせておきましょう。でないと今後あなたの質問には誰も回答しなくなります。 「質問のピントがずれている」という自覚があるのでしたら https://teratail.com/help/question-tips を読んで質問の立案からクローズまでを学んでから質問してください。
KSwordOfHaste

2018/04/20 05:19

マイナスついてしまうのはルールからいってもしかたないと思いますけど、ちょっと気の毒な気もします。「うーん、ちょっと整理しきれずに質問してしまった・・・とりあえずクローズしないと」とあわてて一番間違ったクローズの仕方(自己解決)にしちゃったケースもあるように思います。 解決できそうにない場合は「一番詳しく書いてくれた方とか最初にコメントくれた方」など誰かを選んでBAにするか、どうしても解決を望むなら「回答者に対してその旨コメントした上で」少し時間を掛ける方がよい気がします。慌ててクローズしなくても。 自己解決の解除って今でも不可能なのでしたっけ?
maisumakun

2018/04/20 05:33

いちおう、自己解決でも未解決に戻すことはできるようです(いま確認してみました)。
KSwordOfHaste

2018/04/20 05:36

できるんですね!では自己解決の解除をお勧めします to: 質問者さん お手数をおかけして申し訳ありません。ありがとうございました。 to: maisumakunさん
sazi

2018/04/20 08:06

続くのなら、質問は、 >1),2),3)についてご説明いただくか、解説サイトを紹介していただけないでしょうか。 なので、 それぞれの項に沿った形での回答を希望されているのではないでしょうか。
swordone

2018/04/20 08:10

そもそもの認識がおかしいんだから、それに沿った回答は無意味だと思う。
sazi

2018/04/20 08:15

>無意味だと思う。 ではギャップはどこにあるのだろう?
Zuishin

2018/04/20 08:26

10 進数を丸める時は四捨五入だけど 2 進数は零捨一入なので同じにならないよというのが本質で、IEEE754 の仕組みとか大きくは関係ないんですよね。 問題を解くために浮動小数点が知りたかったのならそれは不要ということは十分にわかったはずですし、ベストアンサーを決めて解決してしかるべきだと思います。 自分でその結論に至ったのなら自己解決の線もありましたが、これだけ回答がついてから同意文での自己解決はあり得ないと私は思います。
swordone

2018/04/20 08:34

表示の最後の桁が3になるべき、という誤った結論から入っているのでおかしくなってる。
maisumakun

2018/04/20 08:40

「質問者が何がわからないのか、回答者としてつかめないままになる」ことが、時折ありますね…
sazi

2018/04/20 08:53

再開するのなら、繰り返しにならないようにと思いついコメントしました。 #回答されている顔ぶれが凄いので、ちょっと勿体なく思ったので。
think49

2018/04/20 23:49

> Javaで10/3.0はなぜ3.3333333333333335と表示されるのかがわかりません。 > ほとんどの回答は自分が調べたものとおなじでした。 この二つを両立させる条件がピンと来ません。 調べても分からなかったのなら「自分が調べたものとおなじ」と判断出来ないはずですよね。 - なぜ調べても分からなかったのか - どこまで分かっているのか - 自分が分かっている(と思っている)ことは本当に正しいのか 問題を細分化して解決する事が必要な気がしますね。
guest

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
cateye

総合スコア6851

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問