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

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

新規登録して質問してみよう
ただいま回答率
85.48%
浮動小数点

浮動小数点は、コンピュータが数値を扱う際に実数を表現する方法のひとつです。 数値を、それぞれの桁の値が並んでいる仮数部と、小数点の場所を示す指数部で表します。

Q&A

解決済

4回答

574閲覧

実数の計算誤差について

max_eipi

総合スコア7

浮動小数点

浮動小数点は、コンピュータが数値を扱う際に実数を表現する方法のひとつです。 数値を、それぞれの桁の値が並んでいる仮数部と、小数点の場所を示す指数部で表します。

0グッド

1クリップ

投稿2019/03/02 11:41

編集2019/03/02 11:44

知りたいこと

  • float型やdouble型の数値を用いて計算した結果の誤差について
  • 誤差が出る理由や要素

コード

どれも(8.2*100)の結果を出力するだけの簡単なプログラム

  • C#

URL: https://paiza.io/projects/C1ogl-OOBCRWlIfK-22f2A
結果: 820

  • PHP

URL: https://paiza.io/projects/HUoUawGobikaCaNU_iPJFw
結果: 820

  • JavaScript

URL: https://paiza.io/projects/NGOJmJDTx3sPnU_-MwD7GA
結果: 819.999...

  • Java

URL: https://paiza.io/projects/AV2kgDYU2atpUD6X8ZEuXQ
結果: 819.999...

質問

上記の結果を見てほしいのですが、8.2*100 をいくつかの言語で計算させたところ、

  • C#,PHP -> 820
  • JavaScript,Java -> 819.999...

という結果になりました。

各言語の標準的な仕様としては、浮動小数点数はIEEE754という規格に準拠しているようなので、
自分の浅はかな考えでは結果に違いが出ないものだと考えていました。
しかし、そんなことはないと言うことが上で実証されているので、「この違いは一体何によって生じるものなのだろうか?」
というのを知りたい所存です。

もちろん、動作環境に依存することもあると思いますが、自身の知見を広げるためという意味でも、
そのようなものも含め「こういうことが考えられるよ」というのがあったらご回答願います。

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

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

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

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

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

guest

回答4

0

ベストアンサー

この違いは一体何によって生じるものなのだろうか?

丸め方の違い(違うのは見かけだけ)です。

実際、PHPでも、厳密には820ではないことは明らかです(3v4l)。

php

1 2$var = 8.2 * 100; 3 4var_dump($var); // float(820) 5var_dump($var - 820); // float(-1.1368683772162E-13)

投稿2019/03/02 12:37

maisumakun

総合スコア145184

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

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

maisumakun

2019/03/03 09:25

JavaScriptでは数値→文字列変換が、「数値→文字列→数値」とした場合に元の値に戻るように定義されていますので、「820にごく近いけど820ではない数」を文字列化すると「820」にはなりません。 https://www.ecma-international.org/ecma-262/9.0/index.html#sec-tostring-applied-to-the-number-type Javaの場合も、得られる文字列は「隣接する値から引数を一意に識別する」ことが可能となっています。 https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Double.html#toString-double-
maisumakun

2019/03/03 09:31

C#のToStringは、特に何も指定しない場合、書式指定「G」と同じ処理が行われますが、これは有効数字15桁での処理となります。 https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/standard-numeric-format-strings?view=netframework-4.7.2#GFormatString doubleは桁数が53ビット相当、10進数に換算すれば16桁弱ありますので、本来違う数が15桁までで丸められて等しくなることがあります。
guest

0

単に、10進文字列に変換する際に、どう丸めるかの差だと思います。
8.2*100を変数に代入して、820を引いた値を表示してみてください。

単に8.2*100-820と書くと、コンパイル時に計算してしまうと思います。

投稿2019/03/02 12:09

otn

総合スコア84559

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

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

otn

2019/03/02 12:15

もしくは、小数点以下の桁数を指定して表示するか。
guest

0

実数の計算では誤差は出ません。
誤差だと見えるのは、
10進数の値を2進数にするところ、
2進数の浮動小数点数を10進数に変換するところ
でおかしくなってます

投稿2019/03/02 11:46

y_waiwai

総合スコア87774

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

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

KSwordOfHaste

2019/03/02 15:37

おそらくy_waiwaiさんは言語によって結果に違いはないということを「誤差が出ない」と表現されたのではないでしょうか?もしそうだとするとその表現は誤解を生むと思います。 例えば精度が10進数で2桁固定のdecimal型のようなものを想定したとして1.1 x 1.1の正確な値は1.21となりますが2桁しか精度がないため結果を丸め1.2にしたとします。そうすると真の値に対して0.01の誤差がでることになりますよね。IEEE754の浮動小数点数も精度は固定(仮数部はケチビットを含め53bit)なので同様のことが起きます。誤差が出ないと表現するとこのような計算誤差が出ないと言っているように聞こてしまいます。 その点を踏まえ回答を訂正してはいかがでしょうか?
y_waiwai

2019/03/02 23:11

「計算」では誤差は出ません。 あくまでもその結果を格納、10進に展開するのに誤差が出るだけです
wwbQzhMkhhgEmhU

2019/03/03 00:08

えっと難しくなってしまうので、とりあえず「出る」でいいのではないでしょうか? 10進数の小数部は2進数で表現できないことがほとんどなので(2で割り切れないから)、y_waiwaiさんのおっしゃることも正しいのですが、多分質問者さんには伝わりません。
KSwordOfHaste

2019/03/03 01:04

y_waiwaiさんがおっしゃる「計算」とは「浮動小数点を演算するプロセッサーが内部に少なくとも105bit以上の仮数部精度があるレジスターを持っていて、乗算に限って与えられた2つの値の演算結果をその内部のレジスターに得るところまで」を言っておられるかも知れませんが、自分は過去に内部精度が80bitのレジスターであるプロセッサがあったことは知ってますが現在の一般的なFPUが仮数部に105bitを持つものばかりなのかどうか知りませんし、乗算ではなく除算なら誤差がでないとは言えません。そもそも乗算に限り、かつ内部的なレジスターに結果を得るところまでを「実数の計算」と表現するのは大雑把すぎて不適切に思えます。それよりはむしろ単純に「実数演算で誤差がある」といった方が質問者さんをはじめとする読者に対して「よりよい認識を伝えることができる」のではないでしょうか? それともy_waiwaiさんがおっしゃる「実数の演算」は自分の解釈とは別の意味合いなのでしょうか?
y_waiwai

2019/03/03 01:17

まあ、あくまで「誤差」の言葉の捉え方の問題なのであんまし深入りはしたくないですが 例えば、FPUのレジスタ長が無限大のものを想定しましょう。 このFPUのもとでは全く誤差なく計算できます。 で、それでは現実の8バイトDoubleに格納します。 現状と結果は全く一緒になりますよね? このときに出る誤差は一体何に由来する誤差とおもわれますか。
pepperleaf

2019/03/03 01:53

ここでの問題点は、理想的な実数演算で無く、現実のCPU(FPU)での実数演算だと思います。 当然、誤差は出ると考えるべきだと思います。
KSwordOfHaste

2019/03/03 02:08

あ、なるほど「実数の演算」は現実のFPUを用いた浮動小数点数の演算ではなく、数学上の演算のことをおっしゃっていたわけですね。自分は質問意図から「実数」を「浮動小数点数」のことを言っているとはやとちりしてしまいました。用語の意味から言えば実数は数学上の概念であって浮動小数点数「でない」ことは確かですね。失礼しました。 (マイナスを付けた方は自分と同じ誤解をしていたのではないでしょうか?納得できたならマイナスを取り消されてはいかがかと思います。)
Zuishin

2019/03/03 03:58

実数の計算では有効桁数を定めるのが通例です。8.2 という 10 進数を 2 進数で表すと無限小数になるので当然丸め誤差が出ます。 誤差が出ているように見えているのではなく、誤差が出ているのです。 しかし、その丸め方は浮動小数点数では標準化されているので、それを使う限りどの言語でも同じ誤差になります。 ただし、その誤差のある数字を 10 進数に直し、さらに文字列化した時にどこで丸めるかの仕様が言語によって違うので、言語によって違う表示となります。 この「言語によって異なる結果」を質問者さんは「誤差」と呼んでいます。この二種類の誤差は分けて考えるべきです。 低評価したのは私ではありませんが、この回答は質問者の尋ねる誤差について言及していません。 「なぜ言語によって結果が違うのか」という質問に答えるものになっていないと考えます。
pepperleaf

2019/03/03 09:12

久々にマイナスしてしまいました。 理由は、質問の意図にそっていないと考えたからです。 条件反射的に押してしまったのですが、コメント見る限り、取り消す必要はなさそうです。
wwbQzhMkhhgEmhU

2019/03/03 10:05

ああ、もう・・・ 純粋に(8.2*100)の表示結果に含まれる誤差の説明をするなら、y_waiwaiさんの説明で何の問題もないと思いますよ。誤差が発生する可能性のある瞬間は、そもそも820という定数を使用しない限りは、 (1)8.2を2進数表記に直した瞬間 (2)それを100倍した後、仮数部の長さで丸めた瞬間 (3)計算結果を10進数に戻した瞬間 の3つです。IEEE754の丸めは複数パターンありますが、通常は最近接丸めのはずなので(未確認)、どの言語、処理系でも共通の値になるという前提を置くと、99999で表示されるケースがある以上、820=1.1001101 * 2^9となるはずがなく、そうなっている以上、(2)の丸め誤差で繰り上がりが発生して表示が変わっているという可能性は排除できます。すると、誤差が発生するのは(1),(3)だけなので、y_waiwaiさんの回答で間違っているとは言えない、という話です。 ぶっちゃけ個人的には、質問者の聞きたい誤差発生部分の説明としては、間違っているわけではないし(疑問は残してしまうと思いますが)、マイナスにするほど?とは思います。間違っていたり、聞いていないことを言っているわけでもないので。。。 各言語と処理系による違いという観点では、otnさんやmaisumakunさんの回答の方が質問者さんの求めている回答に近いとは思います。ただ、基数の違いによる誤差発生要因について触れてるのはy_waiwaiさんだけなので、両方とも本筋だと思います。横槍失礼しました。
guest

0

参考
最近読んだ記事で面白かったので紹介します。
ECMAScriptの浮動小数点数の丸め仕様がスゴい

投稿2019/03/03 04:34

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問