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

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

ただいまの
回答率

90.33%

  • C

    4010questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • 浮動小数点

    6questions

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

浮動小数点数型の誤差?について

解決済

回答 6

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 670

KIYZ

score 10

前提

昨日 C の勉強を始めたばかりの者です。
的確な質問タイトルさえも分からなかったため、意図が伝わりにくい聞き方になってしまっているかもしれませんが、どうかお許し下さい。
(※適切な質問タイトルも教えて頂けると幸いです。)

理解できない現象

実数の桁揃えについて学んでいる時に以下の 2 パターンのコードを実行した時、疑問を抱きました。

パターンA

#include <stdio.h>

int main(void)
{
    double val = 123.45;

    printf("%5.1f\n", val);

    return 0;
}
// 結果:123.5

パターンB

#include <stdio.h>

int main(void)
{
    double val = 123.455;

    printf("%6.2f\n", val);

    return 0;
}
// 結果:123.45

パターン A では小数第二位が四捨五入されて?小数第一位が繰り上げられる?ようですが、パターン B では小数第二位が小数第三位によって繰り上げられないようです。(実行結果が 123.46 にならない)
下記についてご教授のほどよろしくお願い申し上げます。

疑問

  1. それぞれ、なぜこの結果になるのか。
  2. どういう法則が成り立っているのか。
  3. 丸め誤差、打ち切り誤差、情報落ち、桁落ち、等の概念が存在するようですが、今の段階でこれらを掘り下げて勉強するべきか。また、上記コードの法則を理解するためには他にどのようなトピックについて勉強するべきか。
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 6

checkベストアンサー

+2

1) おっしゃる通り、表現の誤差の問題です。

とりあえず、簡単に次のようなプログラムを実行してみました。

#include <stdio.h>

int main(void) {
  printf("%.60f\n", 123.45);
  printf("%.1f\n", 123.45);
  printf("%.60f\n", 123.455);
  printf("%.2f\n", 123.455);
  return 0;
}

これを Wandbox により実行した結果が次の通りです (リンクをクリックすると Wandbox へ飛びます) 。

123.450000000000002842170943040400743484497070312500000000000000
123.5
123.454999999999998294697434175759553909301757812500000000000000
123.45

あまり正確ではないですが、とりあえず、内部表現はこんな感じになっているというイメージをつかんでもらえれば。
これらをそれぞれ四捨五入すると質問のような状態になるわけです。

2) なかなか難しい問題です。このあたりが C 言語の面倒なところといいますか...

まず、 C 言語においては浮動小数点の表現方法が規格により規定されていません。ただ、どのような表現であってもコンピュータの扱える数字が有限である以上、厳密に表せない数というものがあります。そういうときは近い数字で近似的に扱うということがされます。

さて、ほとんどのコンピュータでは主に二進数で数字を扱いますが、十進数でキリよく表される数も、二進数ではキリよく表されないということがあります (我々の知る循環小数のような) 。ちなみに二進数で表せる数は 2 の累乗の和で表せる数です。 123.4 も 123.45 もそうではありませんので、近似的に表されることになります。それが先の実行結果で後半に現れているゴミです。

printf() の書式指定 %f については、指定精度を越える部分については丸められることになっています。
あまり規格に詳しくはないのですが、この丸めがどう行われるかは丸めモードに依存します。丸めモードとは浮動小数の丸め方を指定するものです。最も近い数に丸める、ゼロ方向に丸める、など色々なモードがあります。 (fesetround() 関数で挙動を切り替えることができます。) デフォルトに関する規定は知りませんが、とりあえず我々が使っている環境では何もしなければ最も近い数に丸められます。

これで結果が説明できます。

  • 123.45 を小数点以下 1 桁に丸めるとき

候補としては 123.4 と 123.5 が考えられます。どちらに近いか。小数第 2 位以下を見ると 500...02842... となっていますので、この誤差の分だけ 123.5 に近いです。なのでこちらに丸められます。

  • 123.455 を小数点以下 2 桁に丸めるとき

候補としては 123.45 と 123.46 が考えられます。どちらに近いか。小数第 3 位以下を見ると 499... となっていますので、誤差があるせいで 123.45 に近いです。よってこちらに丸められます。

ちなみにちょうど 0.5 にあたるときは (0.5 は 1/2 なので二進数できっかり表せます) どちらまでの距離も等しいので、どちらに丸めてもよいことになります。ちなみに手元で試すと切り捨てでしたが、 Windows では違うと聞いたり聞かなかったり (※試していません) 。

3) このあたりに関しては経験不足で私では何とも言えないのですが...プログラミングによって何をしたいかによると思います。

例えば数値積分をするとか、計算に重点をおく使い方で、精度ができるだけたくさんいるような場面では、それらの知識はとても大切になってくると思います。情報落ちや桁落ちを防ぐために計算順序を工夫するといったことが常に付きまとってきます。

例えばゲームを作りたいという場面であれば、極端な例でない限りそれほど厳密な精度が要るとは思えません (※何も知らずにしゃべっています) 。少なくとも陰の位置がほんの誤差分だけずれていたり、光の反射が誤差分だけ角度がずれていたとしても気づかないでしょう。
余談ですが、多少の精度を犠牲にして逆平方根を高速に求める手法というのが昔とあるゲームに実装されていたそうです。光の反射を計算するために利用されていたらしいです。このように精度よりむしろ速度などが優先される場合もあると思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/07/17 03:21

    大変ご丁寧にご回答して頂き、誠にありがとうございます。

    >コンピュータでは主に二進数で数字を扱いますが、十進数でキリよく表される数も、二進数ではキリよく表されないということがあります

    そもそもこの辺りのことをしっかり理解できていませんでした。
    指定子の仕様についても詳しく知っておく必要がありそうですね。


    >計算に重点をおく使い方で、精度ができるだけたくさんいるような場面では、それらの知識はとても大切になってくる

    まだご説明の全て理解することはできていませんが、質問で記した疑問と、ご回答の内容を理解するためには何を学べば良いのか、ということは分かってきました。
    今の自分には少しハードルが高いということと、 C の勉強を進める上で今すぐ細部まで完全に理解する必要はなさそうという印象を受けたため、もう少し基礎的な勉強を進めてからここに戻ろうと思います。

    キャンセル

+1

ほとんどの環境では浮動小数点数値などの数値は2進数で表現されています。
そのため、正確に表現できるのは、2のべき乗の数(・・・, 64, 32, 16, 8, 4, 2, 1, 0.5, 0.25, 0.125, 0.0625, ・・・)の有限個の和で表現可能な物だけです(有限個と言っても数値のビット数による個数制限有り)。

123.45 も、123.455 も2のべき乗の有限個の和では表現できません。2進だと無限小数になります。
これは3進法の0.1が、10進法だと0.333333・・・と、無限小数になるのと同じです。
無限小数を有限桁に丸めるため、誤差が生じます。
さらにそれを10進数に変換するときに、丸めの境界でどちらに入るかが違ってくる可能性があります。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/07/17 04:01

    ご回答誠にありがとうございます。

    とても簡潔で的確なご回答だということは何となく分かるのですが、(分からない部分を調べながら読ませて頂きましたが、)私にまだまだ知らないことが多すぎるようで、完全には理解できませんでした。
    一先ずN進法の復習から始めます…。

    キャンセル

  • 2018/07/20 12:29

    「有限桁の10進数で表現できるのは、・・・、1000、100、10、1、0.1、0.01、0.001、・・・・ の有限個の和だけ」
    ということが理解できさえすれば、あとは同じ事です。

    キャンセル

0

今の段階でこれらを掘り下げて勉強するべきか

必要になってから調べてもいいと思います。(今必要なら今ですが・・・)

どのようなトピックについて勉強するべきか

まず、コンピュータ(CPU)内部で浮動小数点数がどのように表されているか? を理解する必要があると思います。実数を2進数で表す場合に、ほとんどの数値に誤差が発生します。詳しくは、下記サイトを確認してみて下さい。

浮動小数点数
浮動小数点数型と誤差

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/07/17 02:55

    ご回答誠にありがとうございます。

    >コンピュータ(CPU)内部で浮動小数点数がどのように表されているか? を理解する必要がある
    参考ページの内容だけでは理解することができませんでしたが、お陰様で分からないことと学ぶべきことを洗い出すことができました。

    キャンセル

0

浮動小数点の誤差、というけど、
勘違いしちゃいけないのが、浮動小数点数に誤差があるってわけではなく、
あくまで10進数に変換するときに誤差が出る、というのを押さえときましょう

んで、以下をよく読んどきましょう。
この中の偶数丸め、というのがたいてい実装されてます

端数処理

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/07/17 03:32

    ご回答誠にありがとうございます。

    >10進数に変換するときに誤差が出る
    理解していませんでした。

    「多分色々と根本的な何かが理解できていない」ということは分かっていたのですが、実際にそれが何かというのがはっきりとは分からなかったところに的確なご指摘を頂けたため、大変助かりました。

    キャンセル

0

それぞれ、なぜこの結果になるのか。 

Ekiさんが回答されているので、そんなところかと。
深いところは、計算機の内部表現とかに依存します。

どういう法則が成り立っているのか。 

計算機の内部表現及び、内部計算処理に依存します。(調べれば、分かりますが、理解はちょっと大変)

今の段階でこれらを掘り下げて勉強するべきか。

どこまで計算精度を要求する処理を行うかに依存しますが、一般的には、浮動小数点演算には、常に誤差がある、程度の理解で良いと考えています。その結果、
. なるべく、整数演算にする。(一桁位なら、小数点をずらす)
. 表示する時は、一桁余分に表示する (判断は見る人)
. 四捨五入などの演算は誤差を考慮する
などしてます。

あと要注意は、桁落ち誤差ですね。例えば、
1.00001 - 1.000009 => 0.000001
この結果の 0.000001 は意味があるかどうか? もしかしたら、ただの浮動小数点誤差かもしれないということです。 if文で、0 と比較すると、≠0 となります。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/07/17 03:41

    ご回答誠にありがとうございます。

    >どこまで計算精度を要求する処理を行うかに依存しますが、一般的には、浮動小数点演算には、常に誤差がある、程度の理解で良い

    まだ細部を理解することはできていませんが、質問で記した疑問と、ご回答の内容を理解するためには何を学べば良いのか、ということは分かってきました。
    こちらで頂いたご回答や色んな記事を読んでみた結果、今の自分には少しハードルが高いということと、 C の勉強を進める上で今すぐ細部まで完全に理解する必要はなさそうという印象を受けたため、もう少し基礎的な勉強を進めてからここに戻ろうと思います。

    キャンセル

0

C言語の規格では浮動小数点型の表現方法は決まっていません。しかし、大抵の場合はIEEE754-1985で、浮動小数点数の値は近似値です。つまり10進数として正確に表現できません。また有効数字部分は倍精度(dobule)の場合は53bit分しかありません。ですが、見掛け上は100桁でも200桁の精度で表示できます。勿論、見掛け上だけで正確ではありません。

仕組みを知りたい場合はIEEE754-1985IEEE754-2008を調べると良いです。

浮動小数点の計算について調べる取っ掛かりとしてはこれを参考に調べると良いと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/07/23 21:04

    >見掛け上は100桁でも200桁の精度で表示できます。勿論、見掛け上だけで正確ではありません。
    こちらの意味が理解できませんが、お陰様で何から調べれば良いのかは分かってきました。

    ご回答誠にありがとうございます。

    キャンセル

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

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

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

  • C

    4010questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • 浮動小数点

    6questions

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