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

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

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

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

for

for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

Q&A

解決済

4回答

9680閲覧

for文のインクリメント・条件判定のタイミングと型キャストの働き

yusukee345

総合スコア31

C

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

for

for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

0グッド

5クリップ

投稿2017/03/09 06:41

以下のような問題を考えます。
「forを用いて1.0から10.0までの値を0.1刻みで表示するコードを作成する。ループ制御には浮動小数点数を使い、条件式部分はキャストを用いて整数式とせよ。」
このようなコードを書いたら上手く実行できました。

#include<stdio.h> void main(){ double f; for (f = 1.0; (int)f < 10;f+=0.1){ printf("%f ",f); } getchar(); }

ここで疑問があります。for文はfor(1;2;3){4}で1→243→243→243の順に実行されますよね。
このforループは
f=1.0→(中略)→9.90表示→9.90+0.1=10.0にインクリメント→(次のループへ)→(int)f<10の判定(10<10)→10.0表示→10.0+0.1=10.1にインクリメント→(次のループへ)→(int)f<10の判定→条件に合わずループ抜ける
という流れになっていますが、9.90+0.1=10.0にインクリメントした後は(int)fは10になるはずなので(int)f<10の条件をクリアできず10.0を表示出来ないはずです。でも実際には10.0は表示されているので条件はクリアできたことになります。
何が起こっているのかを教えて貰えないでしょうか。

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

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

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

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

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

guest

回答4

0

ベストアンサー

こんにちは。

(赤文字は2進数表現です。その他は10進数表現です。)

0.1は2進数に変換すると、循環小数になってしまいます。
0 . 0001 1001 1001 1001 1001 1001 ...
C/C++のdoubleはIEEE754のbinary64で表現する処理系が多いですが、その場合 仮数部は53ビットあります。
1 . 1001 1001 1001 1001 1001 ...
という感じで表現されます。
53%4は3なので最後の1001のセットの10までで有効桁が終わり、その次が0ですから、四捨五入(零捨一入)され01 1001 ...が切り捨てられます。
微妙に0.1より小さい数字を100回足した結果は10より微妙に小さいため、(int)fで小数点下が切り捨てられ、条件を満たさないということになります。

(int)で切り捨てる前にround()で丸めると意図通りに動作する筈です。

投稿2017/03/09 07:10

編集2017/03/09 07:16
Chironian

総合スコア23272

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

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

majiponi

2017/03/09 15:29

roundで丸めると、9.6の時点でループから抜けてしまうのでは? あと、9.99...は10より小さいのに、なぜ「条件文で」キャストしてあるのか、その説明があるとなお親切かと(9.99...9<10ですよね?)
Chironian

2017/03/09 20:09

> roundで丸めると、9.6の時点でループから抜けてしまうのでは? ああ、その通りですね。ご指摘ありがとうございます。 (int)へのキャストはともともキャストされていたからですが、整数で比較した方が速いし、今回のようなトラブルが起きにくいので好ましいですね。 yusukee345さん。 10倍してroundして10で割るか、10倍してroundして100と比較するのがお手軽です。 (int)(round(f*10.)/10.) < 10 (int)round(f*10.) < 100 四捨五入する桁を選べないって不便ですね。
yusukee345

2017/03/10 11:43

回答ありがとうございます。浮動小数点数についてそのような根本的なことをあまり考えたことが無かったです。
guest

0

理解としてはあっていますから大丈夫ですよ。
ソースも正しいと思います。

double型のfが10.0と思われるのにf<10の条件を抜けてしまうのは浮動小数点数型につきものの誤差が原因だと思います。
こちらのページが分かりやすいです。

double型で計算をしてしまうと、10.0に見えても実際には9.99999999999999999999999何がしだったりになってしまうことがあります。
そうするとint型に変換した時9になってしまいます。

会社かどこかの研修であれば、ちょっと話し合ってみると良いでしょう。
(僕ならこういう問題はそういう話すること前提じゃなきゃ出さないです)

投稿2017/03/09 06:54

haru666

総合スコア1591

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

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

yusukee345

2017/03/10 11:48

ありがとうございます。リンク先分かりやすいです。
guest

0

問題の方が浮動小数点数を指定しているので、出題者と話し合ってもらうのが良いと思ったんですよね。

投稿2017/03/10 01:31

haru666

総合スコア1591

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

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

PineMatsu

2017/03/10 07:43

私もそう思いますね。出題の意図が何かしらあると思われますからね。 forのループ変数に浮動小数を使うことの問題点とどうすれば良いのかを考えさせるための出題かもしれません。
yusukee345

2017/03/10 11:47

某C学習書からの問題ですが、出題意図はキャストをどう使うかというとこですね。ただ予想外の問題が隠れていて面白いなとも思います。
guest

0

浮動小数点云々については他の方の回答で間に合ってると思いますので、ちょっと別の視点から。

1.0から10.0までの値を0.1刻みで表示するコードを作成する

通常の日本語で解釈すれば 1.0 ≦ x ≦ 10.0 と1と10を含むことになるので

C

1for(f = 1.0; f <= 10.0; f+=0.1)

と組むのが自然です。

ただ、実際の組み方としては、他の方の指摘にもある通り浮動小数は数学的に正しく全ての数値を表現することが出来ないので、変数をforのループ変数として使うことは避けるべきです。
問題を解釈すると、「1.0~10.0までを0.1刻みで数値を表示する」ということなので、

C

1int i; 2double f = 1.0; 3for(i = 0; i < 10; i++) { 4 printf("%f ",f); 5 f += 0.1; 6}

とすれば、数値の初期値と増分がどういう値であっても確実に10個表示されることになります。(表示個数、初期値、増分を変数にして関数化してもいいと思います)

投稿2017/03/09 23:21

PineMatsu

総合スコア3579

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

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

yusukee345

2017/03/10 11:45

ありがとうございます。キャストを使わないのならそのように書くのがベストでしょうね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.51%

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

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

質問する

関連した質問