- ベストアンサー
加減剰余のオーバーフローについて
今C言語で加減剰余のプログラムを作っていて、 オーバーフローのチェックだけが上手くいきません。 limits.hをインクルードして、オーバーフローのチェック を行いたいのですがどうすればよいのでしょうか? 扱いたい範囲はint型の-2147483648~2147483647です。 ちなみに開発環境はvc++2005です。 一応ソースを載せておきますので、よろしくお願いします。 #pragma warning ( disable : 4996 ) #include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #include <limits.h> int main ( int argc, char *argv[] ) { int start; // 最初に入力する値(引数1) int end; // 最後に入力する値(引数3) int kekka; // 計算結果を格納 int amari; // 除算の余りを格納 int max = INT_MAX; // 表すことが出来る最大値 int min = INT_MIN; // 表すことが出来る最小値 char enzansi; // 入力する演算子 (引数2) char str1[512]; // 格納用の配列を宣言(start用) char str2[512]; // 格納用の配列を宣言(end用) /* 引数の個数チェック */ if ( argc != 4 ) { printf ( "usage : %s start enzansi end\n", argv[0] ); exit ( 0 ); } /* 引数を取得して整数型に変換 */ start = atoi ( argv[1] ); end = atoi ( argv[3] ); /* 引数の取得 */ enzansi = ( argv[2][0] ); sprintf ( str1, "%d", start ); // str1に値を格納 sprintf ( str2, "%d", end ); // str2に値を格納 /* str1とargv[1]による文字列の比較 */ if ( strcmp ( str1, argv[1] ) ) { printf ( "error : 整数を入力して下さい。\n" ); exit ( 0 ); /* str2とargv[3]による文字列の比較 */ } else if ( strcmp ( str2, argv[3] ) ) { printf ( "error : 整数を入力して下さい。\n" ); exit ( 0 ); /* argv[2]が2文字以上の場合 */ } else if ( strlen ( argv[2] ) >= 2 ) { printf ( "error : 演算子を正しく入力して下さい。\n" ); exit ( 0 ); } /* 入力された演算子の条件ごとに計算 */ if ( enzansi == '+' ) { kekka = start + end; } else if ( enzansi == '-' ) { kekka = start - end; } else if ( enzansi == '*' ) { kekka = start * end; } else if ( enzansi == '/' ) { /* startの値を0で除算する場合 */ if ( end == 0 ) { printf ( "error : 0による除算は禁止です。\n" ); exit ( 0 ); } kekka = start % end; // 剰余を求める /* 割り算の結果として余りが出なかった場合 */ if ( kekka == 0 ) { kekka = start / end; printf ( "%d %c %d = %d", start, enzansi, end, kekka ); exit ( 0 ); /* 割り算の結果として余りが出た場合 */ } else if ( kekka != 0 ) { kekka = start / end; amari = start % end; printf ( "%d %c %d = %d余り%d", start, enzansi, end, kekka, amari ); exit ( 0 ); } /* 入力された演算子が異なる場合 */ } else { printf ( "error : '+' '-' '*' '/'のいずれかの演算子を入力して下さい。\n" ); exit ( 0 ); } /* 数値1 演算子 数値2 = 演算結果の形式で出力する */ printf ( "%d %c %d = %d", start, enzansi, end, kekka ); exit ( 0 ); } もし変な部分がありましたら指摘お願いします。 一応四則演算は問題なくできます。
- みんなの回答 (16)
- 専門家の回答
質問者が選んだベストアンサー
★Werner さん、そうですね。注意書きするよりは goto を使うべきでしたね。 ・それで追記としてサブ関数にした方が見やすいと付けましたが言葉が足りませんでしたね。 質問者さんへ。main() 関数の for(;;) の部分がトリッキーでしたので書き直してみました。 エラー表示の switch 文を void error_print(int errno); というサブ関数にしました。 // エラー表示 void error_print( int errno ) { char *errmsg; // エラー文字列を格納 switch ( errno ){ case ERR_USAGE: errmsg = "usage : %s start enzansi end"; break; case ERR_INTEGER: errmsg = "整数を入力して下さい。"; break; case ERR_INTOVER: errmsg = "整数型の範囲を超えました。"; break; case ERR_ENZAN: errmsg = "演算子を正しく入力して下さい。"; break; case ERR_DIVID: errmsg = "0による除算は禁止です。"; break; case ERR_OVERFLOW: errmsg = "オーバーフローが起こります。"; break; } puts( errmsg ); } // main 関数 int main( int argc, char *argv[] ) { int kekka; // 計算結果を格納 int ret; // 戻り値を受け取る /* (1)引数のチェック */ if ( (ret = param_check(argc,argv)) != ERR_SUCCESS ){ error_print( ret ); ←ここを関数にした。 return ret; } /* (2)オーバーフローのチェック */ if ( (ret = overflow_check(INT_MIN,INT_MAX)) != ERR_SUCCESS ){ error_print( ret ); ←ここを関数にした。 return ret; } /* (3)演算子による四則演算 */ kekka = calc( start, enzansi, end ); /* (4)演算結果を表示 */ printf ( "%d %c %d = %d", start, enzansi, end, kekka ); return 0; } その他: ・引き算のオーバーフローのロジックを整理すると (1)正数 - 正数 (2)正数 - 負数 (3)負数 - 正数 (4)負数 - 負数 の4タイプしかありません。 このうち、(1)、(4)は int 型の範囲に納まるためチェックは不要です。 あとは(2)、(3)の符号が異なるときに、それぞれオーバーフローのチェックを分けて 記述すれば良い。詳しくは回答者 No.13 さんのアドバイスをどうぞ。 ・以上。
その他の回答 (15)
- Werner
- ベストアンサー率53% (395/735)
> -2147483648 - -1もオーバーフローになってしまいました。どうすればよいのでしょうか? デバッグしてください。 おかしくなる条件が分かっているのだから、 チェック過程をprintfで出力するとかしてトレースしてみましょう。 (ちょっと流れを見ていて気になったのですが、人に頼りすぎてませんか? これはあなたの研修課題で、本来自分で解決すべきなんですよね? だったらあまり他人に頼りすぎるのは良くないんじゃないかなあ…。 自分で十分考えて分からなかったので尋ねているというならまあいいんじゃないかとは思いますが。 しかし、この文おせっかい以外の何物でもないな^^;) まあ私がANo.13で説明したとおりなら、AもBも負の時は、 > if(Aは正、Bは負) > if(Aは負、Bは正) のどちらでもないからifブロックの中は実行されずオーバーフロー扱いにはならないはずで、 私からはANo.13で説明したとおりに書き直してくださいとしか言えないんですけどね。 それから、私もサンプルコードを書いてみたので載せておきます。 (上で自分で考えろみたいなことを言っておいて載せるのもどうかと思うけど、 せっかく書いたので^^;) ---------------------------------------------------------------------- #include <stdio.h> #include <limits.h> enum OP { ADD, SUB, MUL, DIV }; /* @return 正常時0、overflow時1 */ int is_overflowed(enum OP op, int operand1, int operand2){ switch(op){ case ADD: if(( (operand1>0 && operand2>0) && (operand1>INT_MAX-operand2) ) || ( (operand1<0 && operand2<0) && (operand1<INT_MIN-operand2) )){ return 1; } break; case SUB: if(( (operand1>0 && operand2<0) && (operand1>INT_MAX+operand2) ) || ( (operand1<0 && operand2>0) && (operand1<INT_MIN+operand2) )){ return 1; } break; case MUL: /* チェック時のINT_MIN/-1を避けるために先に確認 */ if( operand2==-1 ){ if(operand1 == INT_MIN) return 1; else return 0; } /* チェック時の0除算に注意、operand2==0の時は除算してはいけない */ if(( operand2>0 && (operand1>INT_MAX/operand2 || operand1<INT_MIN/operand2) ) || ( operand2<0 && (operand1<INT_MAX/operand2 || operand1>INT_MIN/operand2) )){ return 1; } break; case DIV: if(operand2==-1 && operand1 == INT_MIN){ return 1; } break; default: return 2; break; } return 0; } /* チェック結果表示するためのラッパー関数 */ void check_overflow(enum OP op, int operand1, int operand2){ int ret_val; ret_val = is_overflowed(op, operand1, operand2); if(ret_val == 0){ printf("no error: %d %c %d\n", operand1, "+-*/"[op], operand2); } else if(ret_val == 1){ printf("overflow occurred: %d %c %d\n", operand1, "+-*/"[op], operand2); } } int main(void){ check_overflow(ADD , INT_MAX, 1); check_overflow(ADD , INT_MAX, -1); check_overflow(ADD , INT_MIN, 1); check_overflow(ADD , INT_MIN, -1); check_overflow(SUB , INT_MAX, 1); check_overflow(SUB , INT_MAX, -1); check_overflow(SUB , INT_MIN, 1); check_overflow(SUB , INT_MIN, -1); check_overflow(MUL , INT_MAX/2, 2); check_overflow(MUL , INT_MAX/2+1, 2); check_overflow(MUL , -(INT_MIN/2), -2); check_overflow(MUL , -(INT_MIN/2)+1, -2); check_overflow(MUL , INT_MIN/2, 2); check_overflow(MUL , INT_MIN/2-1, 2); check_overflow(MUL , -(INT_MAX/2), -2); check_overflow(MUL , -(INT_MAX/2)-1, -2); check_overflow(MUL , INT_MAX, -1); check_overflow(MUL , INT_MIN, 1); check_overflow(MUL , INT_MIN, -1); /*オペランドを左右交換*/ check_overflow(MUL , 2, INT_MAX/2); check_overflow(MUL , 2, INT_MAX/2+1); check_overflow(MUL , -2, -(INT_MIN/2)); check_overflow(MUL , -2, -(INT_MIN/2)+1); check_overflow(MUL , 2, INT_MIN/2); check_overflow(MUL , 2, INT_MIN/2-1); check_overflow(MUL , -2, -(INT_MAX/2)); check_overflow(MUL , -2, -(INT_MAX/2)-1); check_overflow(MUL , -1, INT_MAX); check_overflow(MUL , 1, INT_MIN); check_overflow(MUL , -1, INT_MIN); check_overflow(DIV , INT_MAX, -1); check_overflow(DIV , INT_MIN, -1); return 0; }
お礼
ありがとうございました。
補足
> if(Aは正、Bは負) > if(Aは負、Bは正) のとおりに条件指定したのですが、-2147483648 - 1とすると、 なぜか符号が変わり、2147483647という結果になってしまいました。 ちなみにオーバーフローのロジックは決まっているということですが、 なぜそうなるのかの理由が分かりません。教えていただけないでしょうか?
- sakusaker7
- ベストアンサー率62% (800/1280)
流れを読まずにぺたり。 #include <stdio.h> #include <limits.h> #ifdef DEBUG #define debug_printf(ARG) fprintf(stderr, ARG); #else #define debug_printf(ARG) #endif enum OP { ADD, SUB, MUL, DIV }; struct CALCDATA { enum OP op; int operand1; int operand2; }; struct CALCDATA data[] = { {ADD, INT_MAX, 1}, {ADD, INT_MAX, 0}, {ADD, INT_MAX-1, 1}, {ADD, -1, 2}, {SUB, INT_MIN, 1}, {SUB, INT_MIN, 0}, {SUB, INT_MIN+1, 1}, {SUB, 2, 3}, {MUL, SHRT_MAX+1, SHRT_MAX+1}, {MUL, SHRT_MAX+1, SHRT_MAX}, {MUL, INT_MIN-1, 3}, {MUL, INT_MIN/2, 2}, {MUL, INT_MIN/2+1, 2}, {MUL, INT_MIN/2, 3}, {MUL, INT_MIN/4, 3}, {DIV, INT_MIN, -1}, }; #define INT_BIT (sizeof (int) * CHAR_BIT) #define MSB (1 << (INT_BIT-1)) unsigned int ones(unsigned int x) { x -= ((x >> 1) & 0x55555555); x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); x = (((x >> 4) + x) & 0x0f0f0f0f); x += (x >> 8); x += (x >> 16); return(x & 0x0000003f); } unsigned int lzc(unsigned int x) { x |= (x >> 1); x |= (x >> 2); x |= (x >> 4); x |= (x >> 8); x |= (x >> 16); return(INT_BIT - ones(x)); } int is_overflowed(struct CALCDATA d) { int d1, d2, d3, d4; d1 = d.operand1; d2 = d.operand2; d3 = 0; d4 = 0; switch (d.op) { case ADD: debug_printf(("### %d + %d\n", d1, d2)); if (d1==0 || d2==0) return 0; if ( (d3=(d1 & MSB) ^ (d2 & MSB)) ^ (((d1+d2) & MSB) ^ d3)) return 1; break; case SUB: debug_printf(("### %d - %d\n", d1, d2)); debug_printf(("\td3=%x\n", !((d1 & MSB) ^ (d2 & MSB)))); debug_printf(("\texpr & MSB=%x\n", ((d1-d2)&MSB))); if (d1==0 || d2==0) return 0; if ((!(d1 & MSB) && (d2 & MSB)) || ((d1 & MSB) && !(d2 & MSB))) { d3 = (d1 - d2) & MSB; if (d3 ^ (d1 & MSB)) return 1; } break; case MUL: debug_printf(("### %d * %d\n", d1, d2)); if (d1 == 0 || d2 == 0) return 0; if ((d1 == INT_MIN && d2 != 0) || (d1 != 0 && d2 == INT_MIN)) return 1; d3 = (d1 ^ d2) & MSB; if (d1 < 0) d1 = ~d1 + 1; if (d2 < 0) d2 = ~d2 + 1; debug_printf(("\t%d * %d (%x)\n", d1, d2, d3)); debug_printf(("\tlzc(d1)=%d * lzc(d2)=%d\n", lzc(d1), lzc(d2))); d4 = lzc(d1) + lzc(d2); if (d4 > INT_BIT) return 0; if (!(d1 & (d1-1)) && !(d2 & (d2-1))) { if (d4 == INT_BIT) return 1; return 0; } if (d4 < INT_BIT) return 1; break; case DIV: debug_printf(("### %d / %d\n", d1, d2)); if (d1 == INT_MIN && d2 == -1) return 1; break; } return 0; } int main() { int i; for (i=0; i<(sizeof data/sizeof data[0]); i++) { printf("Try %d %c %d ", data[i].operand1, data[i].op["+-*/"], data[i].operand2); if (is_overflowed(data[i])) printf("overflow!"); putchar('\n'); } }
お礼
すみません。私にはなんだか難しすぎて 理解できませんでした。
- Werner
- ベストアンサー率53% (395/735)
> ちなみに-2147483647 - 1が-2147483648とならずにオーバーフローとなります。これはどうしようもないのでしょうか? > 掛け算、引き算部分のソースでおかしい部分があるのでしょうか? 以下の部分で、 > /* オーバーフローのチェック */ > if ( ( ( start > 0 ) && ( end < 0 ) ) || ( ( start < 0 ) && ( end > 0 ) ) ) { > if ( abs ( end ) > ( max - abs ( start ) ) ) { > printf ( "error : オーバーフローが起こります。\n" ); > exit( -1 ); > } > } 正-負と負-正をまとめて処理しているのが原因でしょう。 abs使わずにちゃんと場合分けしてみてください。 /* A-Bのオーバーフローチェック */ if(Aは正、Bは負){ /* A-B>INT_MAXでオーバーフロー発生 チェックするためにオーバーフローが起こらない形に式変形*/ if(A>INT_MAX+B){/*Err*/} } if(Aは負、Bは正){ /* A-B<INT_MINでオーバーフロー発生 */ if(A<INT_MIN+B){/*Err*/} } そもそも、startやendがINT_MINで有る可能性がある以上absは使うべきではないと思います。 (abs(INT_MIN)がINT_MAXを超えて表現できなくなるから。 私の環境で試してみたら、abs(INT_MIN)==INT_MINになってしまいました。) なので、同じくabsを使っている加算・乗算部分も書き換えた方がよいでしょう。 あと今更だけど、startやendという変数名は分かりにくい気がしました。 startやendはただのオペランドで、始まるとか終わるという意味は持ってないですよね? これならまだaとbとかの方が良いと個人的には思います。 a、bが味気ないなら、左オペランド、右オペランドのニュアンスを持たせるとか。 (命名のしかたはかなり個人の好みの要素が入るので、あまりこの部分は真剣に考えてくれなくてかまいませんが) > ANo.11 > for ( ; ; ){ ←繰り返し文としては使っていない。注意! そういう注釈が必要な使い方をするくらいなら、 goto ERROR; とかにした方が良くないですか?
お礼
ありがとうございました。 if(Aは正、Bは負){ /* A-B>INT_MAXでオーバーフロー発生 チェックするためにオーバーフローが起こらない形に式変形*/ if(A>INT_MAX+B){/*Err*/} } if(Aは負、Bは正){ /* A-B<INT_MINでオーバーフロー発生 */ if(A<INT_MIN+B){/*Err*/} } というふうにしたら、-2147483648 - 1はオーバーフローのメッセージが 表示されて、成功したのですが、-2147483648 - -1もオーバーフローになってしまいました。どうすればよいのでしょうか?
- Oh-Orange
- ベストアンサー率63% (854/1345)
★追記。 ・エラー表示の switch 文もサブ関数にした方が見やすいかも。 ・以上。
- Oh-Orange
- ベストアンサー率63% (854/1345)
★サブ関数の役割を整理して分割すればよい。 (1)引数のチェック(start,end,enzansi,strtol でのエラーチェックです) (2)演算子によるオーバーフローのチェック(加算,減算,乗算,除算で戻り値にチェック結果を返す) (3)演算子による四則演算の処理(ここではオーバーフローのチェックはなくてよい) (4)演算結果を表示…これは関数にしなくても良いかも。→main() で表示すれば良い。 ・大きく分けて上記の4つの自作関数を作ります。 また、エラーが起きたときや、オーバーフローの printf() 文は main() 関数で記述する方が すっきりします。よって、サブ関数では printf() は基本的に使わずに作ります。 ・下に main() 関数のサンプルを載せます。 サンプル: /* エラー定数の定義 */ #define ERR_SUCCESS (0) // 正常 #define ERR_USAGE (-1) // usage : %s start enzansi end #define ERR_INTEGER (-2) // 整数を入力して下さい。 #define ERR_INTOVER (-3) // 整数型の範囲を超えました。 #define ERR_ENZAN (-4) // 演算子を正しく入力して下さい。 #define ERR_DIVID (-5) // 0による除算は禁止です。 #define ERR_OVERFLOW (-6) // オーバーフローが起こります。 /* 変数をグローバルで定義 */ int start; // 最初に入力する値 int end; // 最後に入力する値 int amari; // 除算の余りを格納 char enzansi; // 入力する演算子 /* 関数のプロトタイプ宣言 */ int param_check( int argc, char *argv[] ); int overflow_check( int min, int max ); int calc( int start, int enzansi, int end ); int main( int argc, char *argv[] ) { char *errmsg; // エラー文字列を格納 int kekka; // 計算結果を格納 int ret; // 戻り値を受け取る for ( ; ; ){ ←繰り返し文としては使っていない。注意! /* (1)引数のチェック */ if ( (ret = param_check(argc,argv)) != ERR_SUCCESS ){ break; ←エラーが発生したので for 文を抜ける } /* (2)オーバーフローのチェック */ if ( (ret = overflow_check(INT_MIN,INT_MAX)) != ERR_SUCCESS ){ break; ←エラーが発生したので for 文を抜ける } /* (3)演算子による四則演算 */ kekka = calc( start, enzansi, end ); /* (4)演算結果を表示 */ printf ( "%d %c %d = %d", start, enzansi, end, kekka ); return 0; } switch ( ret ){ case ERR_USAGE: errmsg = "usage : %s start enzansi end"; break; case ERR_INTEGER: errmsg = "整数を入力して下さい。"; break; case ERR_INTOVER: errmsg = "整数型の範囲を超えました。"; break; case ERR_ENZAN: errmsg = "演算子を正しく入力して下さい。"; break; case ERR_DIVID: errmsg = "0による除算は禁止です。"; break; case ERR_OVERFLOW: errmsg = "オーバーフローが起こります。"; break; } puts( errmsg ); return ret; } その他: ・main 関数だけを紹介しました。 あとはサブ関数の param_check()、overflow_check()、calc() を作成して下さい。 param_check() は引数の個数と strtol() で start、end に整数にセットします。 この関数では ERR_INTEGER、ERR_INTOVER、ERR_ENZAN を返すように作ります。 すべてチェックが正常ならば ERR_SUCCESS 定数を返す。 ・overflow_check() では演算子によるオーバーフローをすべてチェックして オーバーフローが起きる可能性があれば ERR_OVERFLOW を返し、0 で除算しようと した場合は ERR_DIVID を返すようにします。また、オーバーフローしなければ 正常なので ERR_SUCCESS 定数を返す。 それから最小値、最大値を引数で受け取れるようにしておきます。 こうしておけば、main() 関数の overflow_check() 部分で上限や下限を簡単に変更できます。 ・calc() では、引数から start、enzansi、end を受け取って四則演算を行います。 オーバーフローなどのエラーはすべて overflow_check() で行っているため、この関数では エラーなしで単純な四則演算をすれば良いだけです。 switch 文を使えば見やすくなります。もちろん、if、else if、else 構文でも構いません。 最後に: ・グローバル変数にしている start、end、amari、enzansi は1つの構造体で管理すれば もっと分かりやすくなります。構造体が分かるのならば、うまく書き換えてみて下さい。 ・以上。参考に。
お礼
お世話になっております。 構造体はまだわかりません(泣) ユーザー定義関数...勉強してみます。 ちなみに、上司に見てもらったら 「-2147483648 × 1 = オーバーフローが起こります。」 「-2147483648 - 1 = 2147483647」 の結果がおかしいことを申告されてしまいました。 確かに上は-2147483648にならなきゃおかしいし、下はオーバーフローにならないといけないんですよね? 掛け算、引き算部分のソースでおかしい部分があるのでしょうか?
- Oh-Orange
- ベストアンサー率63% (854/1345)
★110行、付近にある最後の『オーバーフロー』チェックがおかしいから。 >/* オーバーフローのチェック */ >if ( min / -1 ) { > printf ( "error4 : オーバーフローが起こります。\n" ); > exit ( -1 ); >} ・の部分の if 判定が間違っています。 間違い⇒if ( min / -1 ){ 正しい⇒if ( (start == min) && (end == -1) ){ これで -2147483648 / 1 は正しく計算されます。 また、-2147483648 / -1 はオーバーフローとして表示されます。 ・あとはサブ関数に分離して分かりやすく main() 関数を書き直すだけですね。 以上。頑張って下さい。
お礼
あぁ本当にありがとうございました。 お礼に100ポイントくらいあげたいです(笑) 後はしっかり意味を理解できるようにしたいと思います。 ちなみに-2147483647 - 1が-2147483648とならずにオーバーフローとなります。これはどうしようもないのでしょうか? お忙しい中本当に申し訳ありません。
補足
すみません。出来ました。本当にありがとうございました。 またお世話になるかもしれませんのでよろしくお願いします。
- Oh-Orange
- ベストアンサー率63% (854/1345)
★エラーメッセージが出なくて正しいと思います。 ・マイナス値(-2147483648)に +120 を足しているのですよ。 ですから加算結果の -2147483528 は int 型の範囲である -2147483648 ~ +2147483647 に 納まります。オーバーフローにはなりませんよ。 ・あと int 型の範囲 -2147483648~ +2147483647 を超えた数字を start もしくは end に入力 してしまうと、強制的に結果が最大値になります。strtol() 関数の仕組みです。 解決策: char *check; int start; start = (int)strtol( argv[1], &check, 10 ); if ( errno != ERANGE ){ if ( *check != '\0' ){ printf( "error : 整数を入力して下さい。\n" ); exit( -1 ); } } else{ printf( "error : 整数型の範囲を超えました。\n" ); exit( -1 ); } ↑この部分を追加すればよい。 ・以上。
お礼
何度も本当にありがとうございます。Oh-Orangeさんに回答して いただいたおかげで本当に助かりました。しかしまだ少し問題が... -2147483648 / 1 などとすると、答えは-2147483648となり、範囲内に収まっているハズなのですが、なぜかオーバーフローです。とメッセージが出ます。同様に-2147483647 - 1等としても答えは上記になると思うのですが、これもオーバーフローです。とメッセージが出ます。以下にソースを記載するのでおかしい部分を指摘していただけないでしょうか? かなり見ずらいと思いますが、よろしくお願いします。
補足
#pragma warning ( disable : 4996 ) #include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #include <limits.h> #include <math.h> int main ( int argc, char *argv[] ) { int start; // 最初に入力する値 int end; // 最後に入力する値 int kekka; // 計算結果を格納 int amari; // 除算の余りを格納 int max = INT_MAX; // 表すことが出来る最大値 int min = INT_MIN; // 表すことが出来る最小値 char *check; char enzansi; // 入力する演算子 /* 引数の個数チェック */ if ( argc != 4 ) { printf ( "usage : %s start enzansi end\n", argv[0] ); exit ( 0 ); } /* 引数を取得して整数型に変換 */ start = atoi ( argv[1] ); end = atoi ( argv[3] ); /* 引数の取得 */ enzansi = ( argv[2][0] ); /* startが整数であるか文字であるかのチェック */ start = ( int ) strtol ( argv[1], &check, 10 ); if ( errno != ERANGE ) { if ( *check != '\0' ) { printf ( "error : 整数を入力して下さい。\n" ); exit ( -1 ); } } else { printf( "error : 整数型の範囲を超えました。\n" ); exit( -1 ); } /* endが整数であるか文字であるかのチェック */ end = ( int ) strtol ( argv[3], &check, 10 ); if ( errno != ERANGE ) { if ( *check != '\0' ) { printf ( "error : 整数を入力して下さい。\n" ); exit ( -1 ); } } else { printf( "error : 整数型の範囲を超えました。\n" ); exit( -1 ); } /* argv[2]が2文字以上の場合 */ if ( strlen ( argv[2] ) >= 2 ) { printf ( "error : 演算子を正しく入力して下さい。\n" ); exit ( -1 ); } /* argv[2]に入力された演算子が'+'の場合 */ if ( enzansi == '+' ) { /* オーバーフローのチェック */ if ( ( ( start > 0 ) && ( end > 0 ) ) || ( ( start < 0 ) && ( end < 0 ) ) ) { if ( abs ( end ) > ( max - abs ( start ) ) ) { printf ( "error : オーバーフローが起こります。\n" ); exit( -1 ); } } kekka = start + end; /* argv[2]に入力された演算子が'-'の場合 */ } else if ( enzansi == '-' ) { /* オーバーフローのチェック */ if ( ( ( start > 0 ) && ( end < 0 ) ) || ( ( start < 0 ) && ( end > 0 ) ) ) { if ( abs ( end ) > ( max - abs ( start ) ) ) { printf ( "error : オーバーフローが起こります。\n" ); exit( -1 ); } } kekka = start - end; /* argv[2]に入力された演算子が'*'の場合 */ } else if ( enzansi == '*' ) { /* オーバーフローのチェック */ if ( abs ( end ) > ( max / abs ( start ) ) ) { printf ( "error : オーバーフローが起こります。\n" ); exit( -1 ); } kekka = start * end; /* argv[2]に入力された演算子が'/'の場合 */ } else if ( enzansi == '/' ) { /* startの値を0で除算する場合 */ if ( end == 0 ) { printf ( "error : 0による除算は禁止です。\n" ); exit ( -1 ); } /* オーバーフローのチェック */ if ( min / -1 ) { printf ( "error : オーバーフローが起こります。\n" ); exit ( -1 ); } kekka = start % end; // 剰余を求める /* 割り算の結果として余りが出なかった場合 */ if ( kekka == 0 ) { kekka = start / end; printf ( "%d %c %d = %d", start, enzansi, end, kekka ); exit ( 0 ); /* 割り算の結果として余りが出た場合 */ } else if ( kekka != 0 ) { kekka = start / end; amari = start % end; printf ( "%d %c %d = %d余り%d", start, enzansi, end, kekka, amari ); exit ( 0 ); } /* 入力された演算子が異なる場合 */ } else { printf ( "error : '+' '-' '*' '/'のいずれかの演算子を入力して下さい。\n" ); exit ( -1 ); } /* 数値1 演算子 数値2 = 演算結果の形式で出力する */ printf ( "%d %c %d = %d", start, enzansi, end, kekka ); exit ( 0 ); }
- Tacosan
- ベストアンサー率23% (3656/15482)
strtol が使えるなら, 変換後 errno == ERANGE のときにオーバーフローしてるみたい.
お礼
ということはどう処理すればよいのでしょうか? 何度もすみません。
補足
こちらから失礼します。サブ関数とは自作関数のことですよね? いろいろ分けて作ろうとしたら訳がわからなくなってしまいました。 具体的にはどのように作成すればよいのでしょうか? int main ( int argc, char *argv[] ) { int start; // 最初に入力する値 int end; // 最後に入力する値 int kekka; // 計算結果を格納 int amari; // 除算の余りを格納 int max = INT_MAX; // 表すことが出来る最大値 int min = INT_MIN; // 表すことが出来る最小値 char *check; // 文字列中に変換不可能な文字があった場合の格納場所 char enzansi; // 入力する演算子 /* 引数の個数チェック */ if ( argc != 4 ) { printf ( "usage : %s start enzansi end\n", argv[0] ); exit ( 0 ); } /* 引数を取得して整数型に変換 */ start = atoi ( argv[1] ); end = atoi ( argv[3] ); /* 引数の取得 */ enzansi = ( argv[2][0] ); /* startが整数であるか文字であるかのチェック */ start = strtol ( argv[1], &check, 10 ); if ( errno != ERANGE ) { if ( *check != '\0' ) { printf ( "error : 整数を入力して下さい。\n" ); exit ( -1 ); } /* startに範囲を超えた値を入力した場合 */ } else { printf( "error : 整数型の範囲を超えました。\n" ); exit( -1 ); } /* endが整数であるか文字であるかのチェック */ end = strtol ( argv[3], &check, 10 ); if ( errno != ERANGE ) { if ( *check != '\0' ) { printf ( "error : 整数を入力して下さい。\n" ); exit ( -1 ); } /* endに範囲を超えた値を入力した場合 */ } else { printf( "error : 整数型の範囲を超えました。\n" ); exit( -1 ); } /* argv[2]が2文字以上の場合 */ if ( strlen ( argv[2] ) >= 2 ) { printf ( "error : 演算子を正しく入力して下さい。\n" ); exit ( -1 ); } /* argv[2]に入力された演算子が'+'の場合 */ if ( enzansi == '+' ) { /* オーバーフローのチェック */ if ( ( ( start > 0 ) && ( end > 0 ) ) || ( ( start < 0 ) && ( end < 0 ) ) ) { if ( abs ( end ) > ( max - abs ( start ) ) ) { printf ( "error : オーバーフローが起こります。\n" ); exit( -1 ); } } kekka = start + end ; /* argv[2]に入力された演算子が'-'の場合 */ } else if ( enzansi == '-' ) { /* オーバーフローのチェック */ if ( ( ( start > 0 ) && ( end < 0 ) ) || ( ( start < 0 ) && ( end > 0 ) ) ) { if ( abs ( end ) < ( max - abs ( start ) ) ) { printf ( "error : オーバーフローが起こります。\n" ); exit( -1 ); } } kekka = start - end; /* argv[2]に入力された演算子が'*'の場合 */ } else if ( enzansi == '*' ) { /* オーバーフローのチェック */ if ( abs ( end ) > ( max / abs ( start ) ) ) { printf ( "error : オーバーフローが起こります。\n" ); exit( -1 ); } kekka = start * end; /* argv[2]に入力された演算子が'/'の場合 */ } else if ( enzansi == '/' ) { /* startの値を0で除算する場合 */ if ( end == 0 ) { printf ( "error : 0による除算は禁止です。\n" ); exit ( -1 ); } /* オーバーフローのチェック */ if ( ( start == min ) && ( end == -1 ) ) { printf ( "error : オーバーフローが起こります。\n" ); exit ( -1 ); } kekka = start % end; // 剰余を求める /* 割り算の結果として余りが出なかった場合 */ if ( kekka == 0 ) { kekka = start / end; printf ( "%d %c %d = %d", start, enzansi, end, kekka ); exit ( 0 ); /* 割り算の結果として余りが出た場合 */ } else if ( kekka != 0 ) { kekka = start / end; amari = start % end; printf ( "%d %c %d = %d余り%d", start, enzansi, end, kekka, amari ); exit ( 0 ); } /* 入力された演算子が異なる場合 */ } else { printf ( "error : '+' '-' '*' '/'のいずれかの演算子を入力して下さい。\n" ); exit ( -1 ); } /* 数値1 演算子 数値2 = 演算結果の形式で出力する */ printf ( "%d %c %d = %d", start, enzansi, end, kekka ); exit ( 0 ); }
- Oh-Orange
- ベストアンサー率63% (854/1345)
★修正⇒2147483648 と 2147483647 が反対でした。 ・間違い⇒int の範囲は +2147483648 ~ -2147483647 の範囲のため… 正しい⇒int の範囲は +2147483647 ~ -2147483648 の範囲のため… それで -2147483648 / -1 という式は、エラーというよりは警告メッセージですよね。 (INT_MIN / -1) なら警告メッセージは出ませんでした。 ・また、(INT_MIN / -1) の結果が INT_MAX を越えると 0 に戻ってしまいますので オーバーフローするように処理すれば良い。 ・以上。
お礼
即レスありがとうございます。なんとか出来ました。最後の質問なのですが、 -2147483648~+2147483647を超えた数字をstartもしくはendに入力してしまうと、強制的に結果が上記の数字になってしまうのですが、どうすれば直るのでしょうか?もしくはそれは無理なのでしょうか? よろしくお願いします。
補足
例えば引数を-214748364000000 + 120などとすると、 -2147483648 + 120 = 2147483528等となってしまい、エラーメッセージが 表示されません。どうすればよいのでしょうか?
- Oh-Orange
- ベストアンサー率63% (854/1345)
★追記。 ・int が 32 ビット長のとき int の範囲は +2147483648 ~ -2147483647 の範囲のため、 >-2147483648 / -1 はエラーになるのです。 ↑上の数値が -2147483647 を下回るためエラーとなるのです。 ・以上。
- 1
- 2
お礼
ありがとうございました。すみません。 いろいろごっちゃになってきたので、まず、オーバーフローだけに ついて、完璧にしたいと思います。absを使わない方が良いのでしょうか? なぜか掛け算でもおかしくなるし、引き算もまだ完璧ではありません。 お忙しいでしょうが、よろしくお願いします。