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

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

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

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

Q&A

解決済

1回答

1106閲覧

C++のpow関数でおかしくなってるのか?

aba

総合スコア14

C++

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

0グッド

0クリップ

投稿2020/01/12 05:45

計算結果が実際に表示されてほしい数値と違うので悩んでいます。

cpp

1#include <stdio.h> 2#include <string.h> 3#include <algorithm> 4#include <iostream> 5#include <string> 6#include <vector> 7#include <math.h> 8using namespace std; 9 10signed main() { 11 double N, sum1 = 0, sum2 = 0; 12 13 cin >> N; 14 15 for (int i = 0; i < 8; i++) { 16 string s; 17 cin >> s; 18 19 for (double j = 0; j < N; j++) { 20 double sum3 = sum1; 21 22 if (i == 2)continue; 23 cout << "i->" << i << " j->" << j << " moji->" << s[j] << endl; 24 25 if (i == 1 && s[j] == '*') { 26 sum1 += pow((double)10, (double)(N - 1) - j) * 5; 27 } 28 else if (i >= 3 && i <= 7 && s[j] == '|') { 29 if (i == 3)sum1 += 0; 30 if (i == 4)sum1 += pow((double)10, (double)(N - 1) - j) * 1; 31 if (i == 5)sum1 += pow((double)10, (double)(N - 1) - j) * 2; 32 if (i == 6)sum1 += pow((double)10, (double)(N - 1) - j) * 3; 33 if (i == 7)sum1 += pow((double)10, (double)(N - 1) - j) * 4; 34 if (i == 7 && j == 0)cout << pow((double)10, (double)(N - 1) - j) * 4 << endl; 35 cout << "i->" << i << " j->" << j << " sum1->" << sum1 << " sum3->" << sum3 << " sa->" << fixed << sum1 - sum3 << endl; 36 } 37 } 38 } 39 40 cout << fixed << "sum1->" << sum1 << endl; 41 42 return 0; 43} 44 45

上記のプログラムに以下のテストケースを入力します

text

118 2**|*****|*****|*** 3||*|||||*|||||*||| 4================== 5*|*|*|*|*|*|*|*|*| 6****|*****|*****|* 7****************** 8**|*****|*****|*** 9|*****|*****|*****

実行結果は以下のようになります。

text

118 2**|*****|*****|*** 3i->0 j->0 moji->* 4i->0 j->1 moji->* 5i->0 j->2 moji->| 6i->0 j->3 moji->* 7i->0 j->4 moji->* 8i->0 j->5 moji->* 9i->0 j->6 moji->* 10i->0 j->7 moji->* 11i->0 j->8 moji->| 12i->0 j->9 moji->* 13i->0 j->10 moji->* 14i->0 j->11 moji->* 15i->0 j->12 moji->* 16i->0 j->13 moji->* 17i->0 j->14 moji->| 18i->0 j->15 moji->* 19i->0 j->16 moji->* 20i->0 j->17 moji->* 21||*|||||*|||||*||| 22i->1 j->0 moji->| 23i->1 j->1 moji->| 24i->1 j->2 moji->* 25i->1 j->3 moji->| 26i->1 j->4 moji->| 27i->1 j->5 moji->| 28i->1 j->6 moji->| 29i->1 j->7 moji->| 30i->1 j->8 moji->* 31i->1 j->9 moji->| 32i->1 j->10 moji->| 33i->1 j->11 moji->| 34i->1 j->12 moji->| 35i->1 j->13 moji->| 36i->1 j->14 moji->* 37i->1 j->15 moji->| 38i->1 j->16 moji->| 39i->1 j->17 moji->| 40================== 41*|*|*|*|*|*|*|*|*| 42i->3 j->0 moji->* 43i->3 j->1 moji->| 44i->3 j->1 sum1->5.00001e+15 sum3->5.00001e+15 sa->0.000000 45i->3 j->2.000000 moji->* 46i->3 j->3.000000 moji->| 47i->3 j->3.000000 sum1->5000005000005000.000000 sum3->5000005000005000.000000 sa->0.000000 48i->3 j->4.000000 moji->* 49i->3 j->5.000000 moji->| 50i->3 j->5.000000 sum1->5000005000005000.000000 sum3->5000005000005000.000000 sa->0.000000 51i->3 j->6.000000 moji->* 52i->3 j->7.000000 moji->| 53i->3 j->7.000000 sum1->5000005000005000.000000 sum3->5000005000005000.000000 sa->0.000000 54i->3 j->8.000000 moji->* 55i->3 j->9.000000 moji->| 56i->3 j->9.000000 sum1->5000005000005000.000000 sum3->5000005000005000.000000 sa->0.000000 57i->3 j->10.000000 moji->* 58i->3 j->11.000000 moji->| 59i->3 j->11.000000 sum1->5000005000005000.000000 sum3->5000005000005000.000000 sa->0.000000 60i->3 j->12.000000 moji->* 61i->3 j->13.000000 moji->| 62i->3 j->13.000000 sum1->5000005000005000.000000 sum3->5000005000005000.000000 sa->0.000000 63i->3 j->14.000000 moji->* 64i->3 j->15.000000 moji->| 65i->3 j->15.000000 sum1->5000005000005000.000000 sum3->5000005000005000.000000 sa->0.000000 66i->3 j->16.000000 moji->* 67i->3 j->17.000000 moji->| 68i->3 j->17.000000 sum1->5000005000005000.000000 sum3->5000005000005000.000000 sa->0.000000 69****|*****|*****|* 70i->4 j->0.000000 moji->* 71i->4 j->1.000000 moji->* 72i->4 j->2.000000 moji->* 73i->4 j->3.000000 moji->* 74i->4 j->4.000000 moji->| 75i->4 j->4.000000 sum1->5010005000005000.000000 sum3->5000005000005000.000000 sa->10000000000000.000000 76i->4 j->5.000000 moji->* 77i->4 j->6.000000 moji->* 78i->4 j->7.000000 moji->* 79i->4 j->8.000000 moji->* 80i->4 j->9.000000 moji->* 81i->4 j->10.000000 moji->| 82i->4 j->10.000000 sum1->5010005010005000.000000 sum3->5010005000005000.000000 sa->10000000.000000 83i->4 j->11.000000 moji->* 84i->4 j->12.000000 moji->* 85i->4 j->13.000000 moji->* 86i->4 j->14.000000 moji->* 87i->4 j->15.000000 moji->* 88i->4 j->16.000000 moji->| 89i->4 j->16.000000 sum1->5010005010005010.000000 sum3->5010005010005000.000000 sa->10.000000 90i->4 j->17.000000 moji->* 91****************** 92i->5 j->0.000000 moji->* 93i->5 j->1.000000 moji->* 94i->5 j->2.000000 moji->* 95i->5 j->3.000000 moji->* 96i->5 j->4.000000 moji->* 97i->5 j->5.000000 moji->* 98i->5 j->6.000000 moji->* 99i->5 j->7.000000 moji->* 100i->5 j->8.000000 moji->* 101i->5 j->9.000000 moji->* 102i->5 j->10.000000 moji->* 103i->5 j->11.000000 moji->* 104i->5 j->12.000000 moji->* 105i->5 j->13.000000 moji->* 106i->5 j->14.000000 moji->* 107i->5 j->15.000000 moji->* 108i->5 j->16.000000 moji->* 109i->5 j->17.000000 moji->* 110**|*****|*****|*** 111i->6 j->0.000000 moji->* 112i->6 j->1.000000 moji->* 113i->6 j->2.000000 moji->| 114i->6 j->2.000000 sum1->8010005010005010.000000 sum3->5010005010005010.000000 sa->3000000000000000.000000 115i->6 j->3.000000 moji->* 116i->6 j->4.000000 moji->* 117i->6 j->5.000000 moji->* 118i->6 j->6.000000 moji->* 119i->6 j->7.000000 moji->* 120i->6 j->8.000000 moji->| 121i->6 j->8.000000 sum1->8010008010005010.000000 sum3->8010005010005010.000000 sa->3000000000.000000 122i->6 j->9.000000 moji->* 123i->6 j->10.000000 moji->* 124i->6 j->11.000000 moji->* 125i->6 j->12.000000 moji->* 126i->6 j->13.000000 moji->* 127i->6 j->14.000000 moji->| 128i->6 j->14.000000 sum1->8010008010008010.000000 sum3->8010008010005010.000000 sa->3000.000000 129i->6 j->15.000000 moji->* 130i->6 j->16.000000 moji->* 131i->6 j->17.000000 moji->* 132|*****|*****|***** 133i->7 j->0.000000 moji->| 134400000000000000000.000000 135i->7 j->0.000000 sum1->408010008010008000.000000 sum3->8010008010008010.000000 sa->400000000000000000.000000 136i->7 j->1.000000 moji->* 137i->7 j->2.000000 moji->* 138i->7 j->3.000000 moji->* 139i->7 j->4.000000 moji->* 140i->7 j->5.000000 moji->* 141i->7 j->6.000000 moji->| 142i->7 j->6.000000 sum1->408010408010008000.000000 sum3->408010008010008000.000000 sa->400000000000.000000 143i->7 j->7.000000 moji->* 144i->7 j->8.000000 moji->* 145i->7 j->9.000000 moji->* 146i->7 j->10.000000 moji->* 147i->7 j->11.000000 moji->* 148i->7 j->12.000000 moji->| 149i->7 j->12.000000 sum1->408010408010408000.000000 sum3->408010408010008000.000000 sa->400000.000000 150i->7 j->13.000000 moji->* 151i->7 j->14.000000 moji->* 152i->7 j->15.000000 moji->* 153i->7 j->16.000000 moji->* 154i->7 j->17.000000 moji->* 155sum1->408010408010408000.000000

一番下にsum1->408010408010408000.000000と表示されてますが本当は
sum1->408010408010408010.000000
と表示されてほしいのです。
i->7 j->0.000000のとき、sum3にはsum1が加算される前の数値を代入しています。sum1にpow関数で求めた数値を加算してsum3とsum1の差を「sa->」で表示しています。sa->を見ると400000000000000000.000000ですがsum1は408010008010008000.000000でsum3は8010008010008010.000000で電卓で計算すると差は399,999,999,999,999,990‬になり10の誤差が生じているように見えます。
なぜ8010008010008010に400000000000000000.000000を足して408010008010008000.000000になっているのか?しかもsa->が400000000000000000.000000になっているのかが分かりません。

i->7 j->0.000000 sum1->408010008010008000.000000 sum3->8010008010008010.000000 sa->400000000000000000.000000

お手数をおかけしますがよろしくお願いいたします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

一般に double

  • 符号部 1 ビット
  • 指数部 11 ビット
  • 仮数部 52 ビット

から成る表現を持っていて、要するに 2 進数で 52 桁 (10 進数だとだいたい 15 桁くらい) の有効精度しかありません。 pow がどうこうではなくて、 double の表現範囲の限界によるものです。

投稿2020/01/12 05:56

SaitoAtsushi

総合スコア5444

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

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

aba

2020/01/12 06:04 編集

doubleをlong doubleにしてみましたが変わりません。long doubleでもダメなのでしょうか?
SaitoAtsushi

2020/01/12 06:35

環境によります。 基本的な算術型について C++ の言語仕様では正確な大きさを定めていません。 最低限度の表現範囲や表現範囲が long double >= double >= float であるといったいくらかの制約はありますが、逆に言えばその制約を満たしさえすれば処理系 (コンパイラ) は言語仕様に準拠していると言えます。 コンピュータというのは千差万別なので、それぞれの都合に応じた選択が出来るように言語仕様には幅が持たさられているのです。 そして long double と double が同じ大きさであるような処理系は珍しくありません。 たとえばマイクロソフトが提供している Microsoft C++ のドキュメントを見ると double と long double は同じ大きさであるということが書かれています。 https://docs.microsoft.com/ja-jp/cpp/cpp/data-type-ranges?view=vs-2019
aba

2020/01/12 07:06

以下のように自分なりにまとめてみました。 long double型の変数が8010008010008010だとする。これに16桁より大きい数字を加算すると正しくない結果になった 4000000000000000を加算 12010008010008010←正しい 40000000000000000を加算 48010008010008008←間違い 400000000000000000を加算 408010008010008000←間違い doubleの範囲を超えた数値でもまったく計算できないのではなく、微妙に違う数値になるケースがあるというのは知らなかった。 回答ありがとうございました。
SHOMI

2020/01/12 10:34

15桁以上の有効桁数がほしいのでしたら、boost::multiprecisionやGMPなどの任意精度計算ライブラリについて調べてみては?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問