cのプログラミング基礎

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
shika

cのプログラミング基礎

#1

投稿記事 by shika » 6年前

なぜ (int)(5.000000) == (int)(4.000000) == 4 なのですか?

アバター
にこよん
記事: 113
登録日時: 7年前
住所: 大阪府
連絡を取る:

Re: cのプログラミング基礎

#2

投稿記事 by にこよん » 6年前

(int)(5.000000) = 5
(int)(4.000000) = 4

ではありませんか?

(int)(5.000000) == (int)(4.000000)
はFalseです
最近は東方風アクションゲームを少しずつ作ってる人です
東方翠風燐FreeDownload⇒http://dxlib.o.oo7.jp/cgi/patio/read.cgi?no=212

アバター
にこよん
記事: 113
登録日時: 7年前
住所: 大阪府
連絡を取る:

Re: cのプログラミング基礎

#3

投稿記事 by にこよん » 6年前

ちなみに

コード:

(int)(4.000000) == 4
はTrueです

(falseは0でtrueは1です)
最近は東方風アクションゲームを少しずつ作ってる人です
東方翠風燐FreeDownload⇒http://dxlib.o.oo7.jp/cgi/patio/read.cgi?no=212

shika

Re: cのプログラミング基礎

#4

投稿記事 by shika » 6年前

win10 で、コマンドプロンプトでMinGWのgccでコンパイル

コード:

#include <stdio.h>

int main (void)
{
	double d;

	for (d = 1.0; (int)d <= 9; d = d + 0.1)
		printf("(int)(%lf) = %d\n", d, (int)d);

	return 0;
}
で、結果の途中は、
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
(int)(3.800000) = 3
(int)(3.900000) = 3
(int)(4.000000) = 4
(int)(4.100000) = 4
(int)(4.200000) = 4
(int)(4.300000) = 4
(int)(4.400000) = 4
(int)(4.500000) = 4
(int)(4.600000) = 4
(int)(4.700000) = 4
(int)(4.800000) = 4
(int)(4.900000) = 4
(int)(5.000000) = 4
(int)(5.100000) = 5
(int)(5.200000) = 5
(int)(5.300000) = 5
(int)(5.400000) = 5
(int)(5.500000) = 5
(int)(5.600000) = 5
(int)(5.700000) = 5
(int)(5.800000) = 5
(int)(5.900000) = 5
(int)(6.000000) = 5
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
です。

box
記事: 2002
登録日時: 13年前

Re: cのプログラミング基礎

#5

投稿記事 by box » 6年前

shika さんが書きました:

コード:

	for (d = 1.0; (int)d <= 9; d = d + 0.1)
(int)(5.000000) = 4
(int)(6.000000) = 5
たぶん浮動小数点数の誤差でしょう。
そもそも10進の0.1を2進で表現するときから誤差が発生します。
(int) 5.000000
と表示しているところは、おそらく、正確には5.000000という数値ではなく4.999999....で、
%lfで表示すると四捨五入か何かで5.000000と表示するが実際の中身は上述のとおり4.999999...なので
intにキャストすると4になってしまう、といったところではないでしょうか。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

shika

Re: cのプログラミング基礎

#6

投稿記事 by shika » 6年前

もしそうなら、3と4の所は、説明が付きません。
だから、質問せざるを得なかったわけです。

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: cのプログラミング基礎

#7

投稿記事 by みけCAT » 6年前

もっと高い精度で表示してみましょう。

コード:

#include <stdio.h>

int main (void)
{
	double d;

	for (d = 1.0; (int)d <= 9; d = d + 0.1)
		printf("d = %.30lf, (int)(%lf) = %d\n", d, d, (int)d);

	return 0;
}
実行結果の一部

コード:

d = 3.800000000000002486899575160351, (int)(3.800000) = 3
d = 3.900000000000002575717417130363, (int)(3.900000) = 3
d = 4.000000000000002664535259100376, (int)(4.000000) = 4
d = 4.100000000000002309263891220326, (int)(4.100000) = 4
d = 4.200000000000001953992523340276, (int)(4.200000) = 4
d = 4.300000000000001598721155460225, (int)(4.300000) = 4
d = 4.400000000000001243449787580175, (int)(4.400000) = 4
d = 4.500000000000000888178419700125, (int)(4.500000) = 4
d = 4.600000000000000532907051820075, (int)(4.600000) = 4
d = 4.700000000000000177635683940025, (int)(4.700000) = 4
d = 4.799999999999999822364316059975, (int)(4.800000) = 4
d = 4.899999999999999467092948179925, (int)(4.900000) = 4
d = 4.999999999999999111821580299875, (int)(5.000000) = 4
d = 5.099999999999998756550212419825, (int)(5.100000) = 5
d = 5.199999999999998401278844539775, (int)(5.200000) = 5
d = 5.299999999999998046007476659724, (int)(5.300000) = 5
d = 5.399999999999997690736108779674, (int)(5.400000) = 5
d = 5.499999999999997335464740899624, (int)(5.500000) = 5
d = 5.599999999999996980193373019574, (int)(5.600000) = 5
d = 5.699999999999996624922005139524, (int)(5.700000) = 5
d = 5.799999999999996269650637259474, (int)(5.800000) = 5
d = 5.899999999999995914379269379424, (int)(5.900000) = 5
d = 5.999999999999995559107901499374, (int)(6.000000) = 5
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

maru
記事: 150
登録日時: 13年前

Re: cのプログラミング基礎

#8

投稿記事 by maru » 6年前

shika さんが書きました:もしそうなら、3と4の所は、説明が付きません。
だから、質問せざるを得なかったわけです。
0.1 を二進数で表現すると 0.001 1001 1001 以下 1001 が繰り返し続きます。16進で表現すると 0.19999999...です。
doubleの1.0 を16進で表現すると 3ff0000000000000 になりますが、これに 0.1 を加えると、3ff199999999999a となります。
これは 0.1 の有効数字以下の桁を零捨一入した結果、最後の桁が繰り上がった結果です。繰り返しパターンの 1001 の先頭を零捨一入したわけです。
これを繰り返して10回足しこむと 4000000000000002 になります。ここで最後の桁に 2 という正の誤差が発生しています。
この (約)2.0 に 0.1を足しこむ時にも加算数に有効数字以下の桁を零捨一入するんですが、被加算数の桁が上がっているため、零捨一入する対象の桁が1桁ずれて 1001 のパターンのうち前のパターンの最後 1 を零捨一入し、足しこんだ値は 4000cccccccccccf になります。ここでも正の誤差が発生します。
これを繰り返して、足しこんだ値が次に桁上がりをした後(約4.0になった時)のことを考えます。足しこんだ値を16進数にすると 4010000000000003 です。正の誤差が発生しているため int に変換しても 4 です。
被加算数の桁が上がっているため、次に 0.1 を足しこむ時、零捨一入する桁は 1001 のパターンの後ろから2番の 0 が対象となり、切り捨てが行われます。つまり今度は負の誤差が発生します。この誤差はこれ以前に発生していた正の誤差より上の桁で発生しているため、これ以降の加算結果はしばらくは10進数で考えた値より小さいものとなります。実際、約 5.0 では 4013ffffffffffff になるので int に変換すると 5 にはなりません。
最後に編集したユーザー maru on 2017年10月29日(日) 15:57 [ 編集 3 回目 ]

maru
記事: 150
登録日時: 13年前

Re: cのプログラミング基礎

#9

投稿記事 by maru » 6年前

maru さんが書きました:この誤差はこれ以前に発生していた正の誤差より上の桁で発生しているため、これ以降の加算結果はしばらくは10進数で考えた値より小さいものとなります。
この部分は正確ではなかったようです。これ以降の結果がすべて10進数で考えた値より小さいものとなるわけではなく、蓄積した誤差が解消された後は10進数で考えた値より小さいものとなるというべきでした。実際に浮動小数点の値が10進数で考えた値より小さくなるのは 4.8 以降になります。

shika

Re: cのプログラミング基礎

#10

投稿記事 by shika » 6年前

詳しい解説をありがとうございます。理由が分かって何よりです。
大変御親切にありがとうございます。

返信

“C言語何でも質問掲示板” へ戻る