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

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

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

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

5回答

3021閲覧

double型の計算で暗黙に四捨五入が行われてしまう

jajaja

総合スコア17

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2020/06/03 15:09

##疑問なこと

cpp

1#include <iostream> 2#include <iomanip> 3 4using namespace std; 5 6int main() 7{ 8 9 double B = 9.79; 10 double C = B + 0.005; 11 12 13 cout << std::fixed << std::setprecision(15) << B << endl; 14 cout << std::fixed << std::setprecision(15) << C << endl; 15 16 17 return 0; 18}

このコードの実行時、出力が

9.789999999999999 9.795000000000000

このように加算を行った時、小数第4位以降のどこかで四捨五入が行われてしまいます。
Cの値が9.79499999999999となるようにするにはどのように変更を加えればよろしいでしょうか。
ご教授いただけると幸いです。

##環境
clang++:Apple clang version 11.0.3 (clang-1103.0.32.29)

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

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

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

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

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

maisumakun

2020/06/03 22:39

えっと、最初の「9.789999999999999」の出力結果は、期待通りということで間違いないですか?
jajaja

2020/06/04 12:36

期待通りで間違い無いです。
maisumakun

2020/06/04 12:49

jajajaさんが、なぜそのような結果を希望しているのかがわかりません(特定の目的があるなら、それを示していただければと思います)。 「9.79」を変数に入れたときの「9.789999999999999」は期待通りだけど、足し算をした後の「9.795000000000000」になるのはよくないというのが、一貫性がないように感じます。
jajaja

2020/06/05 13:01

`9.789999999999999`に`0.005`を足すと`9.79499999999999`と理論上はなるのに、`9.795000000000000`と出力されてしまうことから、私は四捨五入されたと考えました。(ikadzuchiさんの回答から四捨五入されたわけではないと学びました) どのような時に四捨五入されるか気になったので、`9.79499999999999`と意図的に出力する方法について質問しました。 質問の仕方が悪く申し訳ありません。
guest

回答5

0

C++

1#include <iostream> 2 3int main() { 4 double B = 9.79; 5 double C = B + 0.005; 6 7 uint64_t * pB = reinterpret_cast<uint64_t *>(&B); 8 uint64_t * pC = reinterpret_cast<uint64_t *>(&C); 9 10 std::cout << *pB << '\n'; 11 std::cout << *pC << '\n'; 12}

とすると、linux上のg++、 clangでも、Visual Studio でも同じ

4621700898098753044 4621703712848520151

が返ってきたので、表示上の、つまりiostream側の違いですね。Apple clang でも同じかもしれません。

ちなみに、Visual Studioでもstd::setprecision(16)と16桁にすると9.7899999999999999になりました。一つ9が多いですが、Apple clangでも試してみる価値はあるかもしれません。

投稿2020/06/03 17:33

Bearded-Ockham

総合スコア430

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

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

jajaja

2020/06/04 12:30

回答ありがとうございます。 まず後半部分なのですが、私の環境でも`std::setprecision(16)`としたところ`9.7899999999999999`の値を得ることができました。 ありがとうございました。 また前半部分での全く関係ない質問なのですが、`p8`の値が`4621700898098753044`となっているのは小数を無理やり整数にキャストしたからでしょうか?
Bull

2020/06/04 12:38

無理矢理、整数で表記しなくても、 std::cout << std::hexfloat << B << '\n'; std::cout << std::hexfloat << C << '\n'; でもいいと思います。(C++11 以降)
jajaja

2020/06/05 13:24

回答ありがとうございます。 16進数で指数表記をすれば良いのですね! ありがとうございます。
guest

0

ベストアンサー

9.790.005は2進数の浮動小数では正確に表現できません。
任意精度演算ライブラリの使用を検討してみては?

Boost.Multiprecisionの場合

C++

1#include <iostream> 2#include <boost/multiprecision/cpp_dec_float.hpp> 3 4using boost::multiprecision::cpp_dec_float_50; 5int main() 6{ 7 cpp_dec_float_50 B("9.79"); 8 cpp_dec_float_50 C = B + cpp_dec_float_50("0.005"); 9 std::cout << std::fixed << std::setprecision(15) << B << std::endl; 10 std::cout << std::fixed << std::setprecision(15) << C << std::endl; 11 12 B = cpp_dec_float_50("9.789999999999999"); 13 C = B + cpp_dec_float_50("0.005"); 14 std::cout << std::fixed << std::setprecision(15) << B << std::endl; 15 std::cout << std::fixed << std::setprecision(15) << C << std::endl; 16 17 return 0; 18}

結果

9.790000000000000 9.795000000000000 9.789999999999999 9.794999999999999

投稿2020/06/04 07:04

編集2020/06/04 10:05
SHOMI

総合スコア4079

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

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

jajaja

2020/06/04 12:48

回答ありがとうございます。 任意精度演算ライブラリというものがあるのですね。 `9.79499999999999`のような表記を出力する方法だったためベストアンサーとさせていただきます。
guest

0

それはあなたが思っているような四捨五入ではありません。
0.005は2進数では正確に表せず、0.005よりわずかに大きい数になります。
9.789999999999999に0.005よりわずかに大きい数を足して9.795になるのは不思議はありません。
またそもそも「9.789999999999999」などのような数は環境によって表示が変わってもおかしくなく、正しい0.005が足せたとしても10進数表記で見て正確な結果になるとは限りません。


【訂正】
すいません。論理展開に誤りがありました。
0.005が(doubleで)0.005よりわずかに大きい数になるのは事実ですが、これを9.7899(略)に足したところで「わずかに大きい」部分は有効数字の外なので、9.795になるか否かとは無関係でした。

正しくは、
「9.7899(略)に正確な0.005を足したものに最も近いdouble値」と9.7899(略)の差が、0.005よりわずかに大きい
です。

9.7899(略)に0.005よりわずかに小さい値を足しても同様に9.795になるはずです。

投稿2020/06/04 05:11

編集2020/06/06 17:43
ikadzuchi

総合スコア3047

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

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

jajaja

2020/06/04 12:44

回答ありがとうございます。 小数を10進数から2進数に変換すると無限な2進数になってしまう場合、必ずどこかの位置で切り上げ処理が入るという認識で大丈夫でしょうか。
ikadzuchi

2020/06/06 17:44

切り上げとは限りません。数値によって切り下げか切り上げのどちらかが起こります。
jajaja

2020/06/19 05:39

承知いたしました。 ありがとうございます。
guest

0

そもそも9.7950000000000009.79499999999999は同じ数値を表現しています。どうして値の表現が異なるかについては、浮動小数点は10進数ではなく2進数で処理するためです。

投稿2020/06/03 18:34

anndonut

総合スコア667

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

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

jajaja

2020/06/04 12:35

回答ありがとうございます。 `9.79499999999999`と`9.795000000000000`が同じ数を表していることと浮動小数点が2進数として処理されていることは存じております。 ただ、`9.79499999999999`の表記で出力をしたかったため質問させていただきました。
guest

0

long doubleを用いると良いです。

ただ、コンピュータは浮動小数点数を正しく持つことが苦手なため、限界はあります。

投稿2020/06/03 15:13

編集2020/06/03 15:20
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

jajaja

2020/06/03 15:22

回答ありがとうございます。 'long double D = B + 0.005;' とし、変数C,Bと同じように出力したところ '9.795000000000000' とdouble型の時と同じ結果になってしまいました、、
退会済みユーザー

退会済みユーザー

2020/06/03 15:26

あー、申し訳ないです。 環境がMSVCなのですね。僕の環境がgccのため、ちゃんとなっていました。 https://wandbox.org/permlink/M8Oe4tN3w9zViVoT MSVCではlong doubleがdoubleと同じなようです。 https://docs.microsoft.com/ja-jp/cpp/cpp/data-type-ranges?view=vs-2019 long doubleでは厳しそうですね。 double型やlong double型に代入時点で誤差が起きているため、正しく計算するのはなかなか難しいと思います。
退会済みユーザー

退会済みユーザー

2020/06/03 15:35

すみません。タグのVisual Studio CodeをVisual Studioと読み間違えていました。 > clang++:Apple clang version 11.0.3 (clang-1103.0.32.29) 書いていただいていましたね。 clangでも実行してみましたが、正しくなりました。 https://wandbox.org/permlink/0Plb3XEfbM3kkLoM Appleのclangの仕様が違うのかもしれません。 gccなどのlong doubleは16バイト、doubleは8バイトです。 sizeof等でチェックして両方8バイトであればそういうことかな、と思います。
ikadzuchi

2020/06/04 05:04

いいえ、精度を上げても10進数の正確な値を表現できないことに変わりはないので、不規則に四捨五入されるように見えるのは解決しません。 直ったように見えたなら、このケースはそうだったというだけの偶然です。
退会済みユーザー

退会済みユーザー

2020/06/04 05:20

10進数の正確な値を表現できないことは存じています。 このケースに関してはという意味でlong doubleを用いていて、根本的な解決になっていないのは認識しています。
jajaja

2020/06/04 11:46

ただ今手元のmacで確認したところ、long doubleは16バイトでした。 自分の調査ではなぜmacのlong doubleがnnenn0さんの結果と異なるのかは分からなかったので、macではlong doubleの挙動が不明だから使うことを控えた方が良いという認識をすることにします。 回答ありがとうございました。
退会済みユーザー

退会済みユーザー

2020/06/04 11:53 編集

環境依存なので違う可能性もありますが、Bをdoubleにしているのではないでしょうか。 正確な浮動小数点数の計算が行いたい、という意図があったならずれた回答をしてすみません。 浮動小数点数について調べてみるとなぜコンピュータが2進数で正確に値を持てないのか理解できると思います。
jajaja

2020/06/05 13:27

Bの型もdoubleになっているのですが、できませんでした。 いえいえ大変参考になりました、ありがとうございます。 自分でもっと浮動小数点数とlong doubleについて調べてみようと思います。
退会済みユーザー

退会済みユーザー

2020/06/05 15:08

Bをlong doubleにして欲しかったのです… ありがとうございます。
jajaja

2020/06/06 04:43

すみません、読み間違いをしておりました。 Bの型をlong doubleに変更したところ9.79499999999999を得ることができました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問