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

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

ただいまの
回答率

89.50%

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

解決済

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 7,221

strike1217

score 581

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)))
少しの変化もありません・・・

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

+1

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/30 19:00

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

    キャンセル

  • 2017/05/30 20:13

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

    キャンセル

  • 2017/05/30 20:26

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

    キャンセル

+1

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/30 18:41

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

    キャンセル

+1

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

checkベストアンサー

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と考えることができます。

#include <limits.h>
#include <ctype.h>

#define LIMIT (INT_MAX / 10)
#define POSITIVE_LAST_D (INT_MAX % 10)

int another_atoi(const char* str) {
  int sign = 1;
  int last_d = POSITIVE_LAST_D;
  switch (*str) {
  case '-':
    sign = -1;
    last_d++;
  case '+':
    str++;
    break;
  }

  int num = 0;
  for ( ; isdigit(*str); str++) {
    int d = *str - '0';
    if (num >= LIMIT) { // 典型ケースを一度の判定で済ます
      if (num > LIMIT || d > last_d)
        goto OVERFLOW;
    }
    num = num * 10 + d;
  }
  return sign * num;

OVERFLOW: // ...さぼり
  return 0;
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/30 22:36

    int 型オーバーフロー:未定義
    unsigned int 型オーバーフロー:動作が保証されている

    そうなんですか~~

    キャンセル

  • 2017/05/30 22:42

    > 2の補数表現のプロセッサで1の補数表現をしたプログラム(Cで書かれたもの)を動かすことって可能なんですか?

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

    キャンセル

  • 2017/05/30 22:49

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

    キャンセル

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

  • ただいまの回答率 89.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる