
今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 );
}
もし変な部分がありましたら指摘お願いします。
一応四則演算は問題なくできます。
No.6
- 回答日時:
★追記。
・int が 32 ビット長のとき
int の範囲は +2147483648 ~ -2147483647 の範囲のため、
>-2147483648 / -1 はエラーになるのです。
↑上の数値が -2147483647 を下回るためエラーとなるのです。
・以上。
参考URL:http://www.st.rim.or.jp/~phinloda/cqa/cqa5.html
No.5
- 回答日時:
★チェックのアドバイス
>start = atoi( argv[1] );
>sprintf( str1, "%d", start );
>
>if ( strcmp(str1,argv[1]) ){
> printf( "error : 整数を入力して下さい。\n" );
> exit( -1 );
>}
・この部分は、strtol 関数でチェックした方が良い。
strtol 関数ならばエラー検出もできますので、文字列→整数値→文字列の3段階にして
strcmp 関数で文字列が違うとき整数ではないと判定しなくても良い。
つまり、
char *check;
int start;
start = (int)strtol( argv[1], &check, 10 );
if ( errno != ERANGE ){
if ( *check != '\0' ){
printf( "error : 整数を入力して下さい。\n" );
exit( -1 );
}
}
とすれば整数以外の文字列かどうかをチェックできます。
end の場合も同じ要領でチェックします。
・詳しくは下の『参考URL』の【使用例】をどうぞ。
その他:
・あと main() 関数がやたらに長いですね。100 行以上もあります。
(1)引数のチェック部
(2)オーバーフローのチェック部
(3)演算部
(4)表示部
最低でも4つサブ関数を作成して main() 関数から呼び出すようにしたほうが分かりやすいですよ。
ソースを今度、書き換えるときは分けてみて下さい。
・以上。参考に!→下の『参考URL』もどうぞ。
参考URL:http://www9.plala.or.jp/sgwr-t/lib/strtol.html
ありがとうございます。関数は思い通りに動くようになったら
やってみたいと思います。
上記strtol関数で、回答者様のコードで試したところ文字を入力すると
その部分が0になってしまいます。どうすればよいのでしょうか?
質問ばかりで申し訳ありません。
No.4
- 回答日時:
★64ビットの整数を使用しない場合。
・(1)加算…符号が同じとき (チェック値 > (最大値 - 計算値)) ならオーバーフローが起きる。
(2)減算…符号が違うとき (チェック値 < (最大値 - 計算値)) ならオーバーフローが起きる。
(3)乗算…符号に関係なく (チェック値 > (最大値 / 計算値)) ならオーバーフローが起きる。
(4)除算…割る数が 0 ならば除算エラーが起こる。
0 で割ると数学上では、無限大(∞)になりオーバーフローが起きる。除算エラーとするか
オーバーフローさせるかは自由です。
・上記の方法はすべて絶対値でチェックして下さい。
最大値とは INT_MAX 定数、計算値とは今まで計算した結果の値です。今回は start 値です。
・下にサンプルを載せておきます。
サンプル:
/* 入力された演算子の条件ごとに計算 */
amari = 0; ←0 で初期化
switch ( enzansi ){ ←switch 文で記述してみました。
case '+':
if ( ((start > 0) && (end > 0)) || ((start < 0) && (end < 0)) ){
if ( abs(end) > (max - abs(start)) ){
/* end を加算するとオーバーフローする */
exit( -1 );
}
}
kekka = start + end;
break;
case '-':
if ( ((start > 0) && (end < 0)) || ((start < 0) && (end > 0)) ){
if ( abs(end) > (max - abs(start)) ){
/* end を減算するとオーバーフローする */
exit( -1 );
}
}
kekka = start - end;
break;
case '*':
if ( abs(end) > (max / abs(start)) ){
/* end を乗算するとオーバーフローする */
exit( -1 );
}
kekka = start * end;
break;
case '/':
if ( end == 0 ){
/* 0 除算エラーです */
exit( -2 );
}
if ( end == -1 ){
/* Tacosan さんのアドバイスを参考に */
exit( -1 );
}
kekka = start / end;
amari = start % end; ←ここで計算しておく
break;
default:
/* 演算子エラーなど */
exit( -3 );
}
if ( amari == 0 ){ ←加算,減算,乗算,割り切れた除算
printf( "%d %c %d = %d\n", start, enzansi, end, kekka );
}
else{ ←割り切れない除算
printf( "%d %c %d = %d余り%d\n", start, enzansi, end, kekka, amari );
}
解説:
・足し算は start、end の符号が同じときにオーバーフローのチェックを行っています。
チェックの『abs(end) > (max - abs(start))』は絶対値でチェックしています。
『絶対値の end』が『最大値-絶対値の start』よりも大きくなれば、足し算したときに
max を越えるためここでオーバーフローします。
・引き算は start、end の符号が違うときにオーバーフローのチェックを行っています。
チェックの『abs(end) > (max - abs(start))』は絶対値でチェックしています。
『絶対値の end』が『最大値-絶対値の start』よりも大きくなれば、引き算したときに
max を越えるためここでオーバーフローします。
・乗算も考えは同じですが、符号のチェックは必要ないためカットします。
あとは足し算と同じチェックの考えですが、引く代わりに割ります。
つまり『abs(end) > (max / abs(start))』と絶対値でチェックします。
『絶対値の end』が『最大値÷絶対値の start』よりも大きくなれば、掛け算したときに
max を越えるためここでオーバーフローします。
・割り算は 0 除算エラーなどをチェックすればよい。あと Tacosan さんのアドバイス通り
INT_MIN を -1 で割るとき場合によっては INT_MAX を越える可能性があるためチェックを
入れた方がよいかも。
・以上。参考に!
この回答への補足
一応ソースを書きなおしてみたんですが、startとendに
INT_MAXとINT_MINの数値を超えると
if ( strcmp ( str1, argv[1] ) ) {
printf ( "error : 整数を入力して下さい。\n" );
exit ( -1 );
/*str2とargv[3]による文字列の比較 */
} else if ( strcmp ( str2, argv[3] ) ) {
printf ( "error : 整数を入力して下さい。\n" );
exit ( -1 );
}
この部分でエラーが起きてしまいしまいます。
これはstartとendもMAXとMINを超えないようにするしかないのでしょうか?
文章がわかりにくくてすみません。
#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;//最初に入力する値(引数1)
int end;//最後に入力する値(引数3)
int kekka = 0;//計算結果を格納
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 ( -1 );
/*str2とargv[3]による文字列の比較 */
} else if ( strcmp ( str2, argv[3] ) ) {
printf ( "error : 整数を入力して下さい。\n" );
exit ( -1 );
/*argv[2]が2文字以上の場合 */
} else if ( strlen ( argv[2] ) >= 2 ) {
printf ( "error : 演算子を正しく入力して下さい。\n" );
exit ( -1 );
}
/*入力された演算子の条件ごとに計算 */
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;
} 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;
} else if ( enzansi == '*' ) {
if ( abs ( end ) > ( max / abs ( start ) ) ) {
printf ( "error : オーバーフローがおこります。\n" );
exit( -1 );
}
kekka = start * end;
} else if ( enzansi == '/' ) {
/*startの値を0で除算する場合 */
if ( end == 0 ) {
printf ( "error : 0による除算は禁止です。\n" );
exit ( -1 );
}
if ( kekka = 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 );
}
No.2
- 回答日時:
VC++ 2005 だと 64ビットの整数型 (int64 とかそんな感じのやつ) があるので, これを使って #1 のようにするのが簡便でよいのですが, どうしようもなく int だけでやろうとすると, 演算ごとに
・加算においてオーバーフローするのは 2数が同符号のときに限られます. で, a, b > 0 なら b <= INT_MAX - a であること, あるいは a, b < 0 なら b >= INT_MIN + a であることを確認する.
・減算は加算の逆なので似たような感じ.
・乗算は, 「割ったらもとに戻る」でいいのかな?
・除算でオーバーフローするのは INT_MIN / -1 のときに限られます.
のような感じでチェックするしかないような.
この回答への補足
ありがとうございます。INT_MIN / -1は
if ( min / -1 ) {
文
exit ( 0 );
}
のようなチェックでいいのでしょうか?
この方法だと引数に-2147483648 / -1と入力するとエラーがおこります。
どうすればよいでしょうか?
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- C言語・C++・C# バイナリファイルをコピーするのにかかる時間を測りたいのですが実行するとFatel error:gli 2 2022/11/03 01:10
- C言語・C++・C# 並列プログラミングのπ計算について 1 2022/07/16 22:30
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- PHP PHP MySql ページング 2 2022/09/20 06:38
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- C言語・C++・C# C言語 共用体について コマンドライン引数で値を2つ入力したときに、argv[2]の値をUNI u1 4 2022/04/25 20:34
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
doubleからintへのキャスト
-
C言語 コンパイルエラー(文字...
-
C言語:2つの複素数(分数)の四...
-
このどこにバグがあるか、教え...
-
TCP/IP: 非ソケットに対するソ...
-
コマンドライン引数について。
-
分割コンパイルの手順と方法に...
-
リターンキー又は、スペースキ...
-
C言語 文字列の中の文字が条件...
-
複数のシリンダーの同調化にコ...
-
クレーンでのCFブレーキとな...
-
互いに素と負の数
-
PID制御
-
RCフィルタと三角波、方形波
-
数学Aの問題です。 答えは、左...
-
16進数を正負反転する方法
-
SPI 非言語の問題です
-
整合フィルタ(matched filter)...
-
定常応答 と 強制応答 は同...
-
主増幅器と前置増幅器について
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
Expression: nptr != NULL
-
リターンキー又は、スペースキ...
-
getch / putch用のヘッダ
-
TCP/IP: 非ソケットに対するソ...
-
アセンブリプログラムで特定の...
-
C言語プログラミングで・・・
-
C言語のエラーについて
-
'printf':識別子が見つかりませ...
-
C言語 コンパイルエラー(文字...
-
c言語プログラミングで初項を1....
-
c言語についての質問です。
-
加減剰余のオーバーフローについて
-
最も文字数が多い行番号と文字...
-
コンソールAPIのSetConsoleScre...
-
C言語で自作ヘッダーを作ったの...
-
プログラミングが分かりません。
-
C言語のソースコードについて教...
-
クレーンでのCFブレーキとな...
-
SPI 非言語の問題です
-
複数のシリンダーの同調化にコ...
おすすめ情報