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

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

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

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

Q&A

解決済

6回答

9135閲覧

C言語でのfloat型の計算で出る誤差について

退会済みユーザー

退会済みユーザー

総合スコア0

C

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

0グッド

0クリップ

投稿2020/05/31 14:02

c

1#include <stdio.h> 2int main(void){ 3 float b =0; 4 for(int i=0;i<10000;i++){ 5 b+=0.01; 6 } 7 printf("%f",b); 8}

0.01を10000回足すだけのプログラムですが、
結果は100.002953 になります。
float計算する際の誤差を最小限に抑える方法を教えてください。

たとえば数値が0.01刻みと分かっているなら100倍してintで計算して最後に100で割ればよいかなと思っていたのですが、

C

1#include <stdio.h> 2int main(void){ 3 float b =9.44; 4 int c=b*100; 5 printf("%d",c); 6}

これの結果が9.43なんです。
9.44を100倍にした時点で9.43となるのでお手上げです。
どうすれば方法が使われているでしょうか。教えてください。

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

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

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

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

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

guest

回答6

0

9.44を100倍にした時点で9.43となるのでお手上げです。

それ以前です。9.44という値自体を保持できないのですから。

Microsoft C(Visual Studio)で試してみると、
float b =9.44;
で格納した時点で変数に入っているのは0x3d0a1741, 9.43999958038330078125‬だったりするのです。
しかも、この誤差がマイナス方向という決まりはなく、例えば
b=9.43;
とすれば、格納されていたのは0x48e11641, ‭9.43000030517578125‬ でした。

2進数で表現しきれない「誤差」をどのように処理するかは別にC言語の規格レベルで決められている話でもなく(そもそも変数の内部表現も規定されていない処理系依存)、となると一般的に「誤差の影響をいかに少なくするか」というお題で話せるのは「演算を整数の範囲に収める」ぐらいしか無いというのは当然の流れではないでしょうか。

どのように質問を変えましょう...「既存のMicrosoftのCやgccで、float型を使って、小数の範囲の値を誤差なく(あるいは一般的に誤差を少なくするように)扱いたい」という制限をつけたなら、「無理」ということしか言えなくなります。

floatにこだわらないなら誤差を少なくするならdoubleを使うし、処理系をいじっていいなら浮動小数点型の内部処理が10進表現のC言語処理系を開発する、という手段は無いとはいえませんが。
C#だとdecimal型なんてありますね。

投稿2020/05/31 23:25

編集2020/05/31 23:36
thkana

総合スコア7639

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

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

kazuma-s

2020/06/01 00:24

> float b =9.44; で格納した時点で変数に入っているのは0x3d0a1741, 9.43999958038330078125‬だったりするのです。 エンディアンが逆ですね。 41170a3d 9.43999958038330078125 4116e148 9.43000030517578125000
thkana

2020/06/01 03:29 編集

> エンディアン 内容を検証しようという人がエンディアンが違うからわからなくなる...なんてことはないだろうと決めつけて、"見たまま"で書きました。
guest

0

float計算する際の誤差を最小限に抑える方法

 そもそもとして、float型は2進数-10進数の変換誤差がある型であるため、常に有効数字を
意識して計算し、適切な位置で四捨五入しながら使うのが前提となっている型です。よって、
あなたがやっているように、有効数字の桁数によるまるめを行わないでおいて誤差を語ることには
何の意味もありません。

 なぜなら、この「数値のまるめを計算のどこでどのように行うべきか」が「floatで計算する
上で誤差を最小限に抑える方法」に直結しているからです。そして、有効数字でのまるめを
適切に行っているのであれば、あなたが誤差と言っているものはいずれも誤差になりません。

 仮に、有効数字を意識した計算順がどうあるべきか、という話であるとしても、非常に条件付けが
多い話になるため、汎用的に語ることは難しくなります。このあたりを知らないのであれば、
指数部分を分離するなどして10進数のまま計算する(けど計算が遅い)型だけで計算するほうが、
誤差がほぼ出ずに済むのであなたの目的を満たしやすいと思われます。

投稿2020/06/01 01:38

himazin.blm

総合スコア581

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

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

0

誤差がまずい場合は,まずは整数でできないかを考える…… という話は既出ですので……

0.01を10000回足す

というケースに当てはまるかどうか,私自身あやふやですが(間違ってれば鋭い突っ込みが入るハズなので,そちらを参照してください)
誤差を減らす方向の方策としては,
単純に「演算回数を減らす」という手が1つはあるかと.
float b = 0.01f * 10000;なら1回の乗算で済むので,

10000回

というのが動的に決まる回数なのだとしても,とりあえず回数は整数でカウントしておき,必要な時点で0.01にその回数を乗じるというのはどうでしょうか.

不勉強なので,効果のほどを明確に述べることはできませんが,有理数を扱う:
「分子と分母を別々に整数で扱っておいて,本当にfloatとかdoubleでの値が必要になったときだけ,分子/分母として値を作る」というやり方もたまに見かけるような気がします.


9.44を100倍して整数にする側の話は,

int c=b*100;


int c = ( b * 100 + 0.5f );とでもしてやればどうでしょうか.(小数点以下を四捨五入)

投稿2020/06/01 01:24

編集2020/06/01 01:31
fana

総合スコア11658

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

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

0

9.44と書かず、最初から944と100倍した数値を使いましょう。

投稿2020/05/31 14:13

otn

総合スコア84557

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

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

0

ベストアンサー

float計算する際の誤差を最小限に抑える方法を教えてください。

という質問で、最初からfloatを使わなければいいという趣旨の回答が来るんですねこのサイト

これからは整数だけを扱っていこうとおもいます。ありがとうございました

微小な数を足して切り捨てる方法があることは把握していたので、
その微小な数って具体的にどう設定するの?みたいな話がしたかったなあ

これは質問の方法が悪かったと諦めます。こういうのってもう一度質問していいのかな。本文変えても目に触れないだろうしなあ

投稿2020/05/31 14:23

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

otn

2020/05/31 15:01 編集

> float計算する際の誤差を最小限に抑える方法を教えてください。 floatでも整数の範囲(小数部が0)で使えばいいです。そういう趣旨の回答です。 > 微小な数を足して切り捨てる方法があることは把握していたので、 数値を10進数文字列化する段階で丸める方法ということなら、別の質問になりますね。
退会済みユーザー

退会済みユーザー

2020/05/31 14:35 編集

.
angel_p_57

2020/05/31 14:48

自身の質問の悪さを解答者への不満に転嫁する物言いは、正直頂けません。 …0.01刻みの数を扱うことが明らかなら、そりゃ整数で、ってことになるでしょう。 「浮動小数点での誤差を見る例として、例えば0.01を足してみて誤差がこの程度です、これを〇〇程度のレベルに落としたいです」というのがないと、単に浮動小数点に対して無知なのかな、と見られるのが落ちです。 誤差を抑えるのが本当に必要か、という話もあるのですが、抑えるのが重要だというのなら次のような情報を参考にするのが良いのではないでしょうか。 https://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%8F%E3%83%B3%E3%81%AE%E5%8A%A0%E7%AE%97%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0
maisumakun

2020/06/01 00:36

> 最初からfloatを使わなければいいという趣旨の回答が来るんですねこのサイト 趣味的に特殊な条件を追求するためのものは除いて、プログラミングはある目的をこなすために行う実用品です。 そもそもやり方を変えたほうが合理的という場合は、自分としては積極的にそういう回答をする方です。
ikadzuchi

2020/06/01 00:51

そりゃまあ何も返答せず20分で諦めていたら望む回答は得られないでしょうね。 質問が目に留まる人の絶対数が少なすぎますし、しかも一瞬で回答を寄越すような人はしばしば脊髄反射で誤解していたり間違っていたり無意味な回答を出す人だったりします(otnさんがそうだと言っているわけではありません)。 > 微小な数を足して切り捨てる方法があることは把握していたので、 > その微小な数って具体的にどう設定するの?みたいな話がしたかったなあ それは自己解決してから書くものではありません。質問を編集したり回答へのコメントとして書いてください。 > これは質問の方法が悪かったと諦めます。こういうのってもう一度質問していいのかな。本文変えても目に触れないだろうしなあ いいえ、質問の方法が悪かったのではありません。解決の方法が悪かったのです。 もう一度質問してもいいでしょうけれど本文を変えようが何をしようが20分で十分に目に触れることは無いでしょうね。
fana

2020/06/01 01:37

> floatを使わなければいいという趣旨の回答 が望ましくないなら,「おっしゃることはもっともですが,本件はfloatを使うという前提でどうかよろしく」とかコメントすれば済むのではないかと思います. (いかにも「何も言わないとそういう話がまず来そうな内容」なので,質問文にて前提条件に関して念を押しておく等があると良いですね)
Zuishin

2020/06/01 04:03 編集

9.44 を 100 倍した後切り捨ててるから 943 になるので、math.h をインクルードして roundf で四捨五入すれば 944 になります。 二進数と十進数を変換すると誤差が出るのは仕方がないことなので、どこかで丸めなければいけません。 誤差を最も少なくしようと思えば最初から整数を使うのが一番なので、まともな回答者のいるところなら、どこで聞いてもそのような回答がつくでしょう。
guest

0

float型に使われている浮動小数点数というのは、小数点以下含めて2進数で表現されます
しかし、あなたが使っているのは10進数です。
この、10進数と2進数の変換の際に誤差が出ます

ですんで、できるだけ10進2進の変換がかからない形で実行させれば、誤差は小さくなりますね

#ぶっちゃけ、0.01という数値はfloatでは表現できません

投稿2020/05/31 14:12

y_waiwai

総合スコア87774

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問