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

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

ただいまの
回答率

87.59%

実数の計算誤差について

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,119

score 7

知りたいこと

  • 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という規格に準拠しているようなので、
自分の浅はかな考えでは結果に違いが出ないものだと考えていました。
しかし、そんなことはないと言うことが上で実証されているので、「この違いは一体何によって生じるものなのだろうか?」
というのを知りたい所存です。

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+3

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

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

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

$var = 8.2 * 100;

var_dump($var); // float(820)
var_dump($var - 820); // float(-1.1368683772162E-13)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/03 18: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-

    キャンセル

  • 2019/03/03 18: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桁までで丸められて等しくなることがあります。

    キャンセル

+3

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/02 21:15

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

    キャンセル

+1

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/03 12:58

    実数の計算では有効桁数を定めるのが通例です。8.2 という 10 進数を 2 進数で表すと無限小数になるので当然丸め誤差が出ます。

    誤差が出ているように見えているのではなく、誤差が出ているのです。

    しかし、その丸め方は浮動小数点数では標準化されているので、それを使う限りどの言語でも同じ誤差になります。

    ただし、その誤差のある数字を 10 進数に直し、さらに文字列化した時にどこで丸めるかの仕様が言語によって違うので、言語によって違う表示となります。

    この「言語によって異なる結果」を質問者さんは「誤差」と呼んでいます。この二種類の誤差は分けて考えるべきです。

    低評価したのは私ではありませんが、この回答は質問者の尋ねる誤差について言及していません。

    「なぜ言語によって結果が違うのか」という質問に答えるものになっていないと考えます。

    キャンセル

  • 2019/03/03 18:12

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

    キャンセル

  • 2019/03/03 19: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さんだけなので、両方とも本筋だと思います。横槍失礼しました。

    キャンセル

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

  • ただいまの回答率 87.59%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る