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

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

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

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

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

4回答

21625閲覧

算術オーバーフローの対策

strike1217

総合スコア651

C

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

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2017/05/30 09:04

atoi関数を自作する
以前上記のような質問をしました。

さらに改良して、最後の問題に直面しました。

int strtoi(char *str, int *error){ int num = 0, ch = 0; *error = 0; while(isspace(*str)) str++; switch(*str){ case '+': ch = 1; break; case '-': ch = -1; break; } for(;*str; str++){ if(isdigit(*str)){ num = num * 10 +(*str - '0'); // 文字数字変換 if(num > INT_MAX){ num = INT_MAX; goto back_function; } } else{ if(num > 0){ *error = 2; goto back_function; } *error = 1; } } back_function: if(ch == -1) return num * ch; else return num; }

オーバーフロー対策です。
その他はとくに問題なく動作します。

最大値の方だけをまず考えます。

if(num > INT_MAX)
num = INT_MAX;

最初にこのように思いつきました。
しかし、実際にやってみると、
55555555555
your number : -279019293

となってしまいます。
なぜでしょうか??
numの値が変動するからかな・・・?と思ったのですが・・・

どのようにすれば、オーバーフローを防げますか??

他にはこのような技ですね。
if((num < INT_MIN / 10) || (num == INT_MIN / 10) && (*str - 0 > -1 * (INT_MINT % 10)))
少しの変化もありません・・・

どなたか教えてください。

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

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

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

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

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

guest

回答4

0

一番簡単なのは他の方の回答の通りですが、ほかのやり方もないわけではありません。
ややかこしいので取り敢えず正の整数だけ扱うとした場合、最大値は2147483647ですから、

  • 文字列の桁数が10桁未満の場合はオーバーフローしないのでそのまま計算
  • 文字列の桁数が10桁を超える場合は「大きすぎます」とエラーを出す
  • 10桁の場合は、10桁めが2以内であること、10桁目が2の場合は9桁目が1以内であること、・・・・

と処理していけばできなくはありません。
まあ、10桁の場合の処理が面倒ではあります。なので、64bit整数で演算するほうが遥かに楽です(笑)

投稿2017/05/30 10:12

PineMatsu

総合スコア3579

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

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

0

オーバーフローというのは、 intINT_MAX より大きい値を持てないということなのです。 なってしまったらもうその値はどうなってるのかわかりません。 オーバーフローしているとき num の値はすでに信頼できないものになっているので if(num > INT_MAX) num = INT_MAX; というチェックは無意味です。

一番簡単な方法は int よりも充分に大きい整数型 (整数型の大きさは環境によって違うので一概には言えませんがたとえば long long int) を使った上で各桁を処理するごとに INT_MAX を超えていないかチェックするという方法だと思います。

投稿2017/05/30 09:39

SaitoAtsushi

総合スコア5444

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

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

strike1217

2017/05/30 09:41

あああ~~ やはり チェックする時には、すでに変な値に変わってしまうんですね!!
guest

0

いちばん手抜きな方法(処理系依存)として、numlong longで宣言してしまう、という方法があります。

多くの64ビット処理系(WindowsのLLP64やUnix系のLP64)では、intが32ビット、long longが64ビットですので、INT_MAXと同じ桁数ぐらいであればlong longに収まります。

投稿2017/05/30 09:36

maisumakun

総合スコア145184

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

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

strike1217

2017/05/30 09:51

なぜlong long でやる方法が処理系依存なんですか?
maisumakun

2017/05/30 10:01 編集

変数のサイズは、int≦long≦long longということと、intは最低16ビット、long longは最低64ビット、というようなルールがあるだけで、実装依存となっています。 例は少ないですが、「ILP64」といって、intもlongもlong longも64ビット、という環境もあるとのことです(long longも同じ長さなので、intであふれる数がlong longでもあふれてしまいます)。
strike1217

2017/05/30 10:00

ああ~~ 変数の大きさ自体が処理系依存ということですね!
strike1217

2017/05/30 11:13

long long int でもかなり大きな数字を入れるとオーバーフローになりますよね??
maisumakun

2017/05/30 11:26

もちろんそうです。ただ、このコードの場合は、どれだけ大きくても「INT_MAXの10倍まで」入れば充分なので、intが32ビット、long longが64ビットなら余裕で入ります。
guest

0

ベストアンサー

int以外の型を使わない論理も考えることはできます。頭の体操的なものになりますが、質問者さんご自身は論理の練習のためにコードを書いていると思うので・・・。ただ2の補数表現であることは仮定してみました。(それはCの仕様の前提ではないかも知れませんが)

  • INT_MAXを超える結果になると手遅れなので、超える前にチェックする
  • 超える前の数値はどこまで許されるか考える
  • INT_MAX,INT_MINの最後の桁は絶対に0にならない(2の補数表現を仮定したから)

INT_MAX/10,INT_MIN/10の絶対値は同じ値になる。またINT_MINの最後の桁は(INT_MAX%10)より1大きくなる。

基本的にはINT_MAX/10を超えたらoverflow、INT_MAX/10と等しければ最後の桁がINT_MAX%10(負のときはそれ+1)を超えたらoverflowと考えることができます。

C

1#include <limits.h> 2#include <ctype.h> 3 4#define LIMIT (INT_MAX / 10) 5#define POSITIVE_LAST_D (INT_MAX % 10) 6 7int another_atoi(const char* str) { 8 int sign = 1; 9 int last_d = POSITIVE_LAST_D; 10 switch (*str) { 11 case '-': 12 sign = -1; 13 last_d++; 14 case '+': 15 str++; 16 break; 17 } 18 19 int num = 0; 20 for ( ; isdigit(*str); str++) { 21 int d = *str - '0'; 22 if (num >= LIMIT) { // 典型ケースを一度の判定で済ます 23 if (num > LIMIT || d > last_d) 24 goto OVERFLOW; 25 } 26 num = num * 10 + d; 27 } 28 return sign * num; 29 30OVERFLOW: // ...さぼり 31 return 0; 32}

投稿2017/05/30 12:26

KSwordOfHaste

総合スコア18394

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

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

strike1217

2017/05/30 12:27

if((num > int_max.quot) || (num == int_max.quot && *str - '0' > int_max.rem)){ if(ch == 1) num = INT_MAX; else num = INT_MIN; goto back_function; } 自分もたった今これで出来ました。
strike1217

2017/05/30 12:30

最小、最大ともに解決しましたが、これが2の補数表現限定の話とはどういうことでしょうか??
KSwordOfHaste

2017/05/30 12:35

文中に書いた「INT_MAX,INT_MINの最後の桁は絶対に0にならない(2の補数表現を仮定したから) INT_MAX/10,INT_MIN/10の絶対値は同じ値になる。またINT_MINの最後の桁は(INT_MAX%10)より1大きくなる。」というところです。この過程を置かないと正の数と負の数の判定が違ってくる可能性があるかもしれない・・・という程の意味です。
strike1217

2017/05/30 12:47

現在のプロセッサは2の補数として、負数を表すと思うのですが・・・ 1の補数は考える場面はない・・・・と思ってよろしんですかね??
KSwordOfHaste

2017/05/30 12:54 編集

自分はそこまで厳密にCの言語仕様を知らないのですが「2の補数表現と規定されているわけではない」と書かれているのを見て「仮定ということにしておこう」と考え上のようなコメントにしました。本当は言語仕様書をみたかったのですがあいにくすぐに見つけられなかったです。 --- プロセッサを2の補数表現以外で設計する人はいないと思います。合理的でないと思うので・・・
strike1217

2017/05/30 12:56

2の補数表現と規定されているわけではない げ・・・ そうなんですか! わかりました。
maisumakun

2017/05/30 13:12

符号付き整数は、「2の補数」以外に「1の補数」や「符号と絶対値」による表現も認められています(そういう処理系が現実にどれだけあるのかは知りません)。 そういうこともあって、符号付き整数のオーバーフローは「未定義の動作」(なに1つ保証されない、鼻から悪魔が飛び出してもかまわない世界)です。一方、符号なし整数のオーバーフローは「上位ビットをすべて捨てて、変数に入るビットだけを取る」という動作が保証されています。
KSwordOfHaste

2017/05/30 13:24

なるほど、やはり言語仕様では2の補数とは規定されてないのですね。2の補数以外が合理的でないと書きましたがそれはいいすぎなのかしら・・・
strike1217

2017/05/30 13:34

2の補数表現のプロセッサで1の補数表現をしたプログラム(Cで書かれたもの)を動かすことって可能なんですか? プロセッサとの関係性にも依存している気がします。
strike1217

2017/05/30 13:36

int 型オーバーフロー:未定義 unsigned int 型オーバーフロー:動作が保証されている そうなんですか~~
KSwordOfHaste

2017/05/30 13:42

> 2の補数表現のプロセッサで1の補数表現をしたプログラム(Cで書かれたもの)を動かすことって可能なんですか? 例えばIA32で動かすにはIA32用のコンパイラーが必要です。しかしCの言語仕様で2の補数表現が強制されてないので、IA32で1の補数表現を内部表現に用いるコンパイラーを作りたければそうしてもいいと思います。ただしプロセッサの整数演算命令は使わずに演算する羽目になるのは確かです。
strike1217

2017/05/30 13:49

2の補数表現式プロセッサで1の補数表現のプログラムを動かす時は、 2の補数表現式プロセッサが持つ演算命令は使えなく、独自に作るしかないわけですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問