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

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

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

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

浮動小数点

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

Q&A

解決済

6回答

2071閲覧

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

KIYZ

総合スコア17

C

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

浮動小数点

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

0グッド

1クリップ

投稿2018/07/15 16:16

編集2018/07/15 16:29

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

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

パターンA

C

1#include <stdio.h> 2 3int main(void) 4{ 5 double val = 123.45; 6 7 printf("%5.1f\n", val); 8 9 return 0; 10} 11// 結果:123.5

パターンB

C

1#include <stdio.h> 2 3int main(void) 4{ 5 double val = 123.455; 6 7 printf("%6.2f\n", val); 8 9 return 0; 10} 11// 結果:123.45

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

###疑問

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

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

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

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

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

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

guest

回答6

0

ベストアンサー

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

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

c

1#include <stdio.h> 2 3int main(void) { 4 printf("%.60f\n", 123.45); 5 printf("%.1f\n", 123.45); 6 printf("%.60f\n", 123.455); 7 printf("%.2f\n", 123.455); 8 return 0; 9}

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

123.450000000000002842170943040400743484497070312500000000000000 123.5 123.454999999999998294697434175759553909301757812500000000000000 123.45

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

  1. なかなか難しい問題です。このあたりが 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 では違うと聞いたり聞かなかったり (※試していません) 。

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

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

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

投稿2018/07/15 18:32

Eki

総合スコア429

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

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

KIYZ

2018/07/16 18:21

大変ご丁寧にご回答して頂き、誠にありがとうございます。 >コンピュータでは主に二進数で数字を扱いますが、十進数でキリよく表される数も、二進数ではキリよく表されないということがあります そもそもこの辺りのことをしっかり理解できていませんでした。 指定子の仕様についても詳しく知っておく必要がありそうですね。 >計算に重点をおく使い方で、精度ができるだけたくさんいるような場面では、それらの知識はとても大切になってくる まだご説明の全て理解することはできていませんが、質問で記した疑問と、ご回答の内容を理解するためには何を学べば良いのか、ということは分かってきました。 今の自分には少しハードルが高いということと、 C の勉強を進める上で今すぐ細部まで完全に理解する必要はなさそうという印象を受けたため、もう少し基礎的な勉強を進めてからここに戻ろうと思います。
guest

0

ほとんどの環境では浮動小数点数値などの数値は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/16 11:24

otn

総合スコア84505

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

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

KIYZ

2018/07/16 19:01

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

2018/07/20 03:29

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

0

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

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

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

投稿2018/07/20 01:25

YasuoOhgaki

総合スコア51

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

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

KIYZ

2018/07/23 12:04

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

0

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

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

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

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

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

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

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

投稿2018/07/16 01:45

pepperleaf

総合スコア6383

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

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

KIYZ

2018/07/16 18:41

ご回答誠にありがとうございます。 >どこまで計算精度を要求する処理を行うかに依存しますが、一般的には、浮動小数点演算には、常に誤差がある、程度の理解で良い まだ細部を理解することはできていませんが、質問で記した疑問と、ご回答の内容を理解するためには何を学べば良いのか、ということは分かってきました。 こちらで頂いたご回答や色んな記事を読んでみた結果、今の自分には少しハードルが高いということと、 C の勉強を進める上で今すぐ細部まで完全に理解する必要はなさそうという印象を受けたため、もう少し基礎的な勉強を進めてからここに戻ろうと思います。
guest

0

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

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

端数処理

投稿2018/07/15 22:53

y_waiwai

総合スコア87749

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

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

KIYZ

2018/07/16 18:32

ご回答誠にありがとうございます。 >10進数に変換するときに誤差が出る 理解していませんでした。 「多分色々と根本的な何かが理解できていない」ということは分かっていたのですが、実際にそれが何かというのがはっきりとは分からなかったところに的確なご指摘を頂けたため、大変助かりました。
guest

0

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

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

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

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

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

投稿2018/07/15 17:33

cateye

総合スコア6851

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

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

KIYZ

2018/07/16 17:55

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問