float型のオーバーフローについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
cattail
記事: 75
登録日時: 10年前

float型のオーバーフローについて

#1

投稿記事 by cattail » 8年前

お久しぶりです。cattailです。
また少し教えて頂きたいことがあります。
float型のオーバーフローのことなのですが、

float test = 0.0f;

として、ミリ秒間隔で、

test = test + 0.001f;

としています。
オーバーフローを起こした場合、値は無限大(infinity)になるというのをネットで見つけたのですが、よくわかりません。
testのオーバーフローを検出するプログラムはどう書けばいいのでしょうか?
よろしくお願い致します。

超初級者
記事: 56
登録日時: 9年前

Re: float型のオーバーフローについて

#2

投稿記事 by 超初級者 » 8年前

そもそも、float型の最大値である
約10^38秒
も実行時間がかかるプログラムなのでしょうか。

cattail
記事: 75
登録日時: 10年前

Re: float型のオーバーフローについて

#3

投稿記事 by cattail » 8年前

超初級者さんご返事ありがとうございます。
お恥ずかしいことなのですが、
約10^38秒という意味が理解できないのです。
すごく長~い時間は桁あふれなんてしないということでしょうか?
ゲームで何時間かプレイしても大丈夫くらいでしょうか。

あ、今テスト中のプログラムが32768.000で止まりました!
これって、オーバーフローしてますか?
僕のプログラムの他の部分が悪いのかなあ?

cattail
記事: 75
登録日時: 10年前

Re: float型のオーバーフローについて

#4

投稿記事 by cattail » 8年前

オーバーフローを起こした場合、値は無限大(infinity)になるというのは、
最大値で止まるということなのでしょうか?
メッシュをアニメーションさせているのですが、25分くらいでアニメが高速になり、50分くらいで止まってしまいます。
32768.000で止まる前に16384.000のときが25分のときとすると合ってるのかな?
符号付きだとそうなるのでしょうか?
if(test >= 16384.0f){
     test = 0.0f;
}
として実験中です。

cattail
記事: 75
登録日時: 10年前

Re: float型のオーバーフローについて

#5

投稿記事 by cattail » 8年前

どうも解決したみたいです!
ありがとうございました!
float型の上限が32768.000?なのか16384.000?なのかわかりませんが、
if(test >= 16384.0f){
     test = 0.0f;
}
でオーバーフロー?しないで動いています。
ちょっと懐疑的なところもありますが、解決とさせていただきます。
ありがとうございました。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: float型のオーバーフローについて

#6

投稿記事 by softya(ソフト屋) » 8年前

えーと、浮動小数点の有効桁を理解しないといけません。
+ 0.001f;
が誤差扱いで加算できなくなっただけでオーバーフローはしていません。
floatの扱い方が間違っているだけです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: float型のオーバーフローについて

#7

投稿記事 by YuO » 8年前

softya(ソフト屋)さんの書かれている通り,単純に情報落ちが発生しているのでしょう。

コード:

#include <stdio.h>

int main(void) {
	float current = 0.0f;
	
	for (float old = -1.f; old != current; old = current, current += 0.001f)
		;
	printf("%f\n", current);

	return 0;
}
というコードを実行すると,Ide.one上(gcc 5.1)では32768.000000と表示されます。
浮動小数点数の比較に!=を使っているので誤差の違いがあれば加算が続くようにしているのですが,それでも上記の値が表示されます。
# 普通はfabsとかで差をとって大きさ比較する
つまり,32768.00000f + 0.001f == 32768.00000fとなっています。

浮動小数点数を使う以上,誤差は必ず出てくるので,特有の誤差についてちゃんと調べておくことをお薦めします。

cattail
記事: 75
登録日時: 10年前

Re: float型のオーバーフローについて

#8

投稿記事 by cattail » 8年前

softya(ソフト屋)さん、
YuOさん、
ご返事頂いてありがとうございます!
浮動小数点の有効桁とか、浮動小数点の特有の誤差とか全然知りませんでした。
今ネットで検索して調べてみたのですが、本当の値と近似値とか難しくてまるでわかりませんでした。
大変恐れ入りますが、
+ 0.001f;
を誤差扱いで加算できなくならないようにするにはどうすればいいのでしょうか?
または、
if(test >= 16384.0f){
     test = 0.0f;
}
とやっても誤差内なのでしょうか?
申し訳ありませんが教えていただけないでしょうか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: float型のオーバーフローについて

#9

投稿記事 by softya(ソフト屋) » 8年前

有効桁は2進数では正確ですが、10進数では約7桁とアバウトになります。まぁ、6桁で使うのが普通です。
で、桁数ですが小数部なくして考えてください。
+ 0.001f;なら1000倍しましょう。
1000倍した16384.0fは
16384000ですので、
12345678と8桁あります。
これは有効桁を超えるので利用できません。

と言うことで、加算で有効桁数が多く必要ならintを使いましょう。
floatは乗算除算に向いています。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

cattail
記事: 75
登録日時: 10年前

Re: float型のオーバーフローについて

#10

投稿記事 by cattail » 8年前

softya(ソフト屋) さん、
教えて頂きありがとうございます!
以下、間違っていると恥ずかしいですが笑わないでー。

>10進数では約7桁とアバウトになります。
7桁以上が表現できないからということでしょうか?

>有効桁を超えるので利用できません。
1000倍してintにして計算の時だけ比較する、のかな?

算数が不得意なのです。
明日、実際にやってみたいと思います。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: float型のオーバーフローについて

#11

投稿記事 by softya(ソフト屋) » 8年前

元々2進数なので、10進数では6.9・・・と少数点の付く桁数になります。
なので、7桁使うと誤差出るかもしれないし、大丈夫かもしれない。組み合わせ次第です。
数学的には正確に問題が出るか計算できますが、そこまで知りたくないなら7桁以上はやばい!ぐらいでよいです。

>1000倍してintにして計算の時だけ比較する、のかな?
それで十分です。常に1000倍で表示だけ工夫すればよいでしょう。
ただ、どこまで上限値が行くのかで64bit整数が必要になるかもしれません。
提示された情報にはありませんでしたので。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

cattail
記事: 75
登録日時: 10年前

Re: float型のオーバーフローについて

#12

投稿記事 by cattail » 8年前

softya(ソフト屋)さん、
おはようございます!
色々ありがとうございます!

>それで十分です。常に1000倍で表示だけ工夫すればよいでしょう。
>ただ、どこまで上限値が行くのかで64bit整数が必要になるかもしれません。
やってみる前から、すごく自信が出てきました!
これからやってみます!
後ほど報告させていただきますね。

cattail
記事: 75
登録日時: 10年前

Re: float型のオーバーフローについて

#13

投稿記事 by cattail » 8年前

なんか混乱して停滞中です。

>1000倍した16384.0fは
>16384000ですので、
>12345678と8桁あります。
ということは、

if(test >= 16384.0f){
     test = 0.0f;
}

の16384.0も16384.000まで使用しているので有効ケタではないということでしょうか?
つまり、
0に+0.001をする有効ケタは、0.0から999.999までの最大6ケタで、

if(test >= 999.999f){
     test = 0.0f;
}

とすれば安心でしょうか?

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

Re: float型のオーバーフローについて

#14

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

cattail さんが書きました: if(test >= 999.999f){
     test = 0.0f;
}

とすれば安心でしょうか?
浮動小数点数を工夫せずに比較するのは、演算誤差により「間違う」リスクがあり、安心ではありません。
(リーディングゼロを含めて)6桁固定の数を扱うのであれば、32ビットであることが保証された整数のint32_tを用いた「固定小数点数」を用いるといいでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

cattail
記事: 75
登録日時: 10年前

Re: float型のオーバーフローについて

#15

投稿記事 by cattail » 8年前

みけCATさん、
ありがとうございます!

int32_tをVS8では使えないのでしょうか?
DWORDで以下のようにしたらどうでしょうか?

コード:

DWORD a = 0;
float b = 0;

b = test;   //testはミリ秒で0.001加算される変数
b = b * 1000;
a = DWORD(b)


if(a >= 999999){
     test = 0.0f;
}

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

Re: float型のオーバーフローについて

#16

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

cattail さんが書きました:DWORDで以下のようにしたらどうでしょうか?
あまり意味無いでしょう。そもそも、セミコロンが抜けていたり全角スペースが入っていたりする時点でダメです。
無駄な浮動小数点演算はなるべく避けましょう。

コード:

DWORD test = 0;
float b = 0;

b = test / 1000.0f; //testはミリ秒で1加算される変数
if(test >= 999999){
    test = 0;
}
もしくは、全くfloat型を使わずに文字列にするなら

コード:

DWORD test = 0;
char b[12];

//testはミリ秒で1加算される変数
//sprintf(b, "%03d.%03d", (int)(test / 1000), (int)(test % 1000));
//sprintfが使える環境なら、以下の8行の代わりに上のsprintfを用いるべき
//sprintfが使えない場合でも、ループにするなど改善の余地はある
b[0] = (test / 100000) % 10 + '0';
b[1] = (test /  10000) % 10 + '0';
b[2] = (test /   1000) % 10 + '0';
b[3] = '.';
b[4] = (test /    100) % 10 + '0';
b[5] = (test /     10) % 10 + '0';
b[6] = (test /      1) % 10 + '0'; // 1で割るのは無駄だが、コードの統一感を出す
b[7] = '\0';

if(test >= 999999){
    test = 0;
}
オフトピック
条件式はこれで大丈夫なのかな?999.999「の次」に000.000にしたいわけじゃないのかな?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

cattail
記事: 75
登録日時: 10年前

Re: float型のオーバーフローについて

#17

投稿記事 by cattail » 8年前

みけCATさん、
色々書いて頂いてすみません。

コード:

DWORD test = 0;
float b = 0;
 
b = test / 1000.0f; //testはミリ秒で1加算される変数
if(test >= 999999){
    test = 0;
}
書いて頂いた上のコードでは、加算される変数自体をDWORDで持って処理するということでしょうか?

それからまさに000.000にしたいので、test = 0;のあとにb = 0;として
実際に処理に使用するfloat変数をbにすればいいということでしょうか?

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

Re: float型のオーバーフローについて

#18

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

cattail さんが書きました:書いて頂いた上のコードでは、加算される変数自体をDWORDで持って処理するということでしょうか?
はい。
cattail さんが書きました:それからまさに000.000にしたいので、test = 0;のあとにb = 0;として
実際に処理に使用するfloat変数をbにすればいいということでしょうか?
すいません。それだと二度手間なので、変換処理の前に0にする処理を持ってくるといいでしょう。

コード:

DWORD test = 0;
float b = 0;

//testはミリ秒で1加算される変数
if(test >= 999999){
    test = 0;
}
b = test / 1000.0f;
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

cattail
記事: 75
登録日時: 10年前

Re: float型のオーバーフローについて

#19

投稿記事 by cattail » 8年前

みけCATさん、
何度も何度もすみませんでした!
書いて頂いたコードでやってみます!

普段から簡潔なコードを心がけていないので、「あ、そうか」の連発で。
自分もそういうふうにコードを書いていきたいと思います。
ただ、ちょっと頭が付いていかないので限界もあるかもしれないです。

今作っているゲームにfloatの演算がたくさんあるので直すのが大変です!

皆様、ありがとうございました!

閉鎖

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