2012-09-01 8 views
24

可能性の重複:
How can I check if multiplying two numbers in Java will cause an overflow?Javaコードで整数オーバーフローを防ぐ方法は?

私は*+操作を使用するJavaクラスのメソッドを、持っていると仮定します。

 
int foo(int a, int b) { 
    ... // some calculations with + and * 
} 

fooにオーバーフローが発生しないようにする方法を教えてください。

 
int sum(int a, int b) { 
    int c = a + b; 
    if (a > 0 && b > 0 && c < 0) 
    throw new MyOverfowException(a, b) 
    return c; 
} 

int prod(int a, int b) { 
    int c = a * b; 
    if (a > 0 && b > 0 && c < 0) 
    throw new MyOverfowException(a, b) 
    return c; 
} 

は何intオーバーフローがJavaメソッドで発生していないことを確認するために、より良い方法があります。

は私がBigDecimalを使用するか、またはすべての+と*のような「ラッパー」で置き換えることができますいずれかを推測しますか?

+3

なぜゼロ以下になっているかチェックしますか? intは0よりもかなり下になる可能性があります。 – 11684

+0

正確には、これはintの範囲です:-2,147,483,648から2,147,483,647 – 11684

+0

値を固定しますか? –

答えて

17

エンジニアリングの観点からは難しい問題です。

Secure Codingサイトでは推奨しています。前提条件の

  • 使用します。つまり、オーバーフローが不可能となるよう入力をレンジチェックし、次のより大きなプリミティブ整数型を使用して個々の算術演算を行い、オーバーフローを明示的にチェックするか、または
  • BigIntegerを使用します。

このDr Dobbs articleは、明示的なオーバーフローチェックで各プリミティブ演算を行うプリミティブ算術メソッドのライブラリを作成することを示唆しています。しかし、著者は、バイトコードの書き換えを使用して算術バイトコードをオーバーフローチェックを組み込んだ同等のメソッドへの呼び出しに置き換えることを提案することでさらに進んでいます。

残念ながら、Javaではオーバーフローチェックをネイティブに有効にする方法はありません。(ただし、C、C++などの他の多くの言語でも同じことが適用されます)

+0

2番目の回答は私の答えです – Alnitak

+1

"エンジニアリングの観点からは難しい問題です。" - これはそれほど難しいことではありません。すべての操作後にオーバーフローレジスタフラグをチェックするマシンコードを生成するだけです。これがC#の「チェック済み」ブロックの機能です。問題は、Javaがそれをオプションとして提供しているわけではないということです。 – Rich

+0

@Rich - Javaコンパイラを変更する立場にいないと難しいです。ほとんどの人はそうではありません! –

5

合計:bがintに格納できる最大値とaの値の差より大きいかどうかを確認します。 aおよび/またはbが負数である場合、(i)差異チェックのためにすでにオーバーフローしないように注意し、(ii)最小値について同様のチェックを実行する必要があります。

プロダクト:それほど難しくありません。整数を2つの長さの整数に分割します(つまり、intが32ビットの場合、ビットマスキングとシフトを使用して2つの16ビット数に分割します)。その後、乗算を行い、結果が32ビットに収まるかどうかを調べます。

一時的な結果のために単にlongを取りたくないという条件のすべてです。

20

オーバーフローをチェックする1つの方法は、オペランドをより大きな型(元のオペランドのビット長の2倍)に昇格させてから操作を実行し、結果の値が元の型に対して大きすぎるかどうかを確認し、例えば

int sum(int a, int b) { 
    long r = (long)a + b; 
    if (r >>> 32 != 0) { // no sign extension 
     throw new MyOverflowException(a, b); 
    } 
    return (int)r; 
} 

あなたの元の型がlongある場合は、その大きなタイプとしてBigInteger使用する必要があると思います。

+0

私はこの答えは古いですが、より大きな型自体がオーバーフローし、より小さい型に対して通常のように見える範囲で終了する可能性があるため、この解決策は動作することが保証されていません。 – yitzih

+0

@yitzihあなたは間違っています - 2つの(正の)整数の加算は、最長のオペランドより1ビット長い値を超えることはできません。 "大きな型自体がオーバーフローする"という問題の制約を与えられた方法はありません – Alnitak

+0

@yitzih私は乗算も関与していることを忘れていましたが、2つの31ビット正の整数の積は62ビットを超えることはできません。 – Alnitak

3

aとbの両方が正または負であり、a + bの符号がaの符号と等しくなく、 b、オーバーフローが発生します。このルールを使用して、オーバーフローが発生したかどうかを判断し、例外をスローすることができます。あなたがこの展示をキャッチするとき、前の答えに見られる方法に従ってそれを扱うことができます。 もう1つの方法は、オーバーフローしない最大範囲タイプを使用して操作を行うことです。 Integer間の演算には長い時間を使うことができます。

関連する問題