- ベストアンサー
0除算して、落ちるプログラムと落ちないプログラム
コンパイラ : cc 環境 : Linux 0除算して、落ちるプログラムと落ちないプログラムが あるのですが、何が違いを生んでいるのでしょうか?
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
>0除算して、落ちるプログラムと落ちないプログラムが >あるのですが、何が違いを生んでいるのでしょうか? 「いつ演算しているか?」と「どうやって演算しているか?」により、複雑に違いを生みます。 ・いつ演算しているか? 「定数同士の演算式」は、コンパイラの最適化によって「計算済みの定数」に置き換わる場合があります。 そしてそれは「処理系依存」なので「それをするコンパイラもあれば、それをしないコンパイラもある」のです。 で、最適化時に「0除算」と判った時に、どういうコードに最適化されるかも「処理系依存」です。 コンパイラによって「0/0」を「除数が何かチェックせず、0は何で割っても0になるから、0/?はすべて0」に最適化してしまう手抜きなコンパイラもあれば「実行時に0除算を行わせ、0で割ったとの実行時例外を、あえて出すようにしている」と言うコンパイラもあります。 つまり「コンパイラによって、コンパイル時に0にされてしまう場合」と「コンパイル時には計算されず、実行時に計算される場合」があるのです。 そしてそれらは「コンパイルオプション」によって変更出来るので、同じプラットフォーム、同じコンパイル環境でも、指定したオプションやデフォルトオプションが何になっているかで、差が出ます。 ・どうやって演算しているか? int同士の割り算、float同士の割り算、double同士の割り算は、すべて「計算の方法」が異なります。 int同士の割り算は、通常、CPUの「除算命令」で実行します。 前述の最適化による「単なる0にする」と言うのが行われていなければ、例外が発生するでしょう。 そして、デフォルトの例外処理ルーチンを書き変えていないなら、ライブラリのデフォルトの例外処理ルーチンが「Floating point exception」などの表示を行ってプログラムを終了します。 float同士の割り算、double同士の割り算は、通常、浮動小数点演算ライブラリが呼ばれます。 ライブラリでは「浮動小数点演算コプロセッサがあるなら、コプロセッサで計算させ、コプロセッサがないならソフトウェアで演算する」と言う処理をします。 コプロセッサに計算させるCPU命令を実行した場合、0除算の演算結果は「0以外/0は無限大(inf)」になり「0/0は非数(nan)」になります。例外は発生しません。 コプロセッサがなく、ソフトウェアで演算した場合も、0除算の演算結果はコプロセッサがある時と同じです(ソフトウェアで「エミュレート」しているのだから、同じにならなくては困る) >・intの場合、分子の値によっても落ちるかどうかが変わる。 数学的には「非0÷0=符号付の無限大」「0÷0=未定義(非数、数ではない)」です。 つまり、被除数(分子)が「非0」か「0」かで、結果が変わります。 そして、C言語の仕様では「intでの0除算の結果は、処理系に依存する」事になっています。つまり「何が起こるか判らない」のです。 しかし、floatやdoubleは「無限大」や「非数」をサポートしているので、例外は発生したりしません。 ちゃんと、数学の通りに「非0÷0=符号付の無限大」「0÷0=非数」と言う計算結果になります。 >floatやdoubleはどうやってこの情報(inf, nan)を持っているのかが次の疑問になりました(- -; IEEE754(IEEE二進化浮動小数点数演算標準)の仕様に従った、浮動小数点の数値は「正負の符号」「指数部」「仮数部」の3つで出来ています。 そして、それぞれ3つの部分の値がどうなっているかで、以下のようになっています。 ・符号が0、指数部が0、仮数部が0=正の0 ・符号が1、指数部が0、仮数部が0=負の0 ・符号が0、指数部が0、仮数部が非0=正の非正規化数 ・符号が1、指数部が0、仮数部が非0=負の非正規化数 ・符号が0、指数部が0以外かつ最大値以外、仮数部が任意=正の正規化数 ・符号が1、指数部が0以外かつ最大値以外、仮数部が任意=負の正規化数 ・符号が0、指数部が最大値、仮数部が0=正の無限大(inf) ・符号が1、指数部が最大値、仮数部が0=負の無限大(inf) ・符号が0、指数部が最大値、仮数部が非0=非数(nan) ・符号が1、指数部が最大値、仮数部が非0=非数(nan) このように「指数部と仮数部の値」によって「無限大」「非数」を表現しています。
その他の回答 (4)
- eroermine
- ベストアンサー率18% (83/444)
intel 86系の場合整数でゼロ除算しても SIGFPEが出ますね。 だからこれをつければ落ちない。 signal(SIGFPE, ゼロ除算処理関数のアドレス);
- jacta
- ベストアンサー率26% (845/3158)
例えば、 #include <stdio.h> int main() { double x = 1.0/0; printf("%f\n", x); return 0; } といったコードであればxに無限大が格納されますので、プログラムは落ちません。
お礼
下記パターンの実験をしました。(gccでコンパイルしましたが、 ccと全ての結果が同じなのかは不明ですが。。) はじめに自分が落ちると言っていたプログラムはソース2のものでした。 また落ちないと言っていたのはソース6のものでした。 そのため、片方では落ちるし、片方では落ちないとなるようでした。。 この結果からは、 ・型によって0除算しても落ちるかどうかが変わる。 →float, doubleはどんな時でも落ちない。 ・intの場合、分子の値によっても落ちるかどうかが変わる。 ということが分かりました。(?がついているのはcodepadで実験したので あまり自信がないという意味です。) floatやdoubleはどうやってこの情報(inf, nan)を持っているのかが次の疑問に なりました(- -; ■実験 ●ソース1:double, 分子は0でない → inf →落ちない? ●ソース2:int , 分子は0でない → Floating point exception →落ちる? ●ソース3:float , 分子は0でない → inf →落ちない? ●ソース4:double, 分子は0 → nan →落ちない ●ソース5:int , 分子は0 → 0 →落ちない ●ソース6:float , 分子は0 → nan →落ちない ●ソース1: #include <stdio.h> int main() { double x = 1.0/0; printf("%f\n", x); return 0; } 【実行結果】 inf ●ソース2: #include <stdio.h> int main() { int x = 1/0; printf("%d\n", x); return 0; } 【実行結果】 Floating point exception ●ソース3: #include <stdio.h> int main() { float x = 1.0/0; printf("%f\n", x); return 0; } 【実行結果】 inf ●ソース4: #include <stdio.h> int main() { double x = 0.0/0; printf("%f\n", x); return 0; } 【実行結果】 nan ●ソース5: #include <stdio.h> int main() { int x = 0/0; printf("%d\n", x); return 0; } 【実行結果】 0 ●ソース6: #include <stdio.h> int main() { float x = 0.0/0; printf("%f\n", x); return 0; } 【実行結果】 nan
- asuncion
- ベストアンサー率33% (2127/6289)
>0除算して、落ちるプログラムと落ちないプログラムがある それらのソースコードを載せてみる、というお気持ちはありますか?
- taka37777
- ベストアンサー率30% (166/544)
0除算して落ちる、落ちないプログラムというのは何を示しているのでしょうか? ・同じソースをコンパイルしても、動作が異なる ・ソースは異なる coreを吐く、吐かない 動作が止まる、止まらない 0除算にならないように通常は演算の前でチェックを入れるようにします。coreファイルを吐く、吐かないというのであればsignalを受け取って終了処理をしているからcoreファイルを吐かないという事になると思います。
補足
補足いたします。 ソースは異なります。 一方は動作がとまりますが、 一方は継続します。 coreを吐く吐かないでは ありません。
お礼
あまりに高度すぎてちょっと理解できませんが、状況により結果は変わってくる ということと捉えました。(かなり大雑把と思いますが。。) ご回答ありがとうございました。