int i = 1024 * 1024 * 1024 * 1024がエラーなしでコンパイルされるのはなぜですか?


152

の制限intは-2147483648〜2147483647です。

入力すると

int i = 2147483648;

次に、Eclipseは「2147483648」の下に赤い下線を表示します。

しかし、これを行うと:

int i = 1024 * 1024 * 1024 * 1024;

正常にコンパイルされます。

public class Test {
    public static void main(String[] args) {        

        int i = 2147483648;                   // error
        int j = 1024 * 1024 * 1024 * 1024;    // no error

    }
}

多分それはJavaの基本的な質問ですが、なぜ2番目のバリアントがエラーを出さないのか分かりません。


10
コンパイラが通常、計算を最適化として単一の値に「折りたたむ」場合でも、結果がオーバーフローになる場合は、最適化によってプログラムの動作が変更されることはないため、そうなりません。
Hot Licks 14

1
そしてそれ解釈することができません2147483648:このリテラルは意味がありません。
DenysSéguret2014

1
そして、Javaは整数のオーバーフローを報告しません-操作は静かに「失敗」します。
Hot Licks 14

5
@JacobKrall:C#は、checked-nessがオンになっているかどうかに関係なく、これを欠陥として報告します。定数式のみで構成されるすべての計算は、チェックされていない領域内を除き、自動的にチェックされます。
Eric Lippert、2014

54
StackOverflowで「なぜしない」の質問をすることはお勧めしません。彼らは答えるのが難しい。「なぜしないのか」という質問は、世界が明らかにそうではない方法であるべきであり、そのようになるには正当な理由がある必要があることを前提としています。この仮定はほとんど有効ではありません。より正確な質問は、「仕様のどのセクションで定数整数演算がどのように計算されるかを説明していますか?」または「Javaでは整数オーバーフローはどのように処理されますか?」
Eric Lippert、2014

回答:


233

そのステートメントには何も問題はありません。4つの数値を乗算してintに割り当てるだけで、オーバーフローが発生するだけです。これは、コンパイル時に境界チェックされる単一のリテラルを割り当てることとは異なります。

エラーを引き起こすのは範囲外のリテラルであり、割り当てではありません。

System.out.println(2147483648);        // error
System.out.println(2147483647 + 1);    // no error

対照的に、longリテラルはうまくコンパイルされます:

System.out.println(2147483648L);       // no error

定数式であるため、実際には結果コンパイル時にも計算されます。1024 * 1024 * 1024 * 1024

int i = 1024 * 1024 * 1024 * 1024;

になる:

   0: iconst_0      
   1: istore_1      

結果(0)は単にロードおよび格納され、乗算は行われないことに注意してください。


JLS§3.10.1から(コメントで取り上げてくれた@ChrisKに感謝):

タイプの10進リテラルが(2 31intより大きい場合、または10進リテラルが単項マイナス演算子のオペランド以外の場所にある場合は、コンパイル時エラーになります(§15.15.4)。21474836482147483648


12
そして、JLSが言う乗算について、整数の乗算がオーバーフローする場合、結果は、いくつかの十分に大きい2の補数形式で表されるように、数学積の下位ビットになります。その結果、オーバーフローが発生した場合、結果の符号は2つのオペランド値の数学積の符号と同じにならない場合があります。
クリスK 14

3
すばらしい答えです。一部の人々は、オーバーフローが何らかのエラーまたは失敗であるという印象を持っているようですが、そうではありません。
Wouter Lievens、2014

3
@ iowatiger08言語セマンティクスは、JVMに依存しないJLSによって概説されます(そのため、どのJVMを使用するかは関係ありません)。
arshajii 14

4
@WouterLievens、オーバーフロー、完全なエラー状態ではないにしても、通常は「異常な」状態です。これは有限精度の数学の結果であり、ほとんどの人は数学をするときに直感的に起こるとは期待していません。など-1 + 1、場合によっては無害です。しかし、1024^4それは彼らが見ていると期待するものからはほど遠い、全く予期しない結果で人々を盲目にするかもしれません。ユーザーには少なくとも警告または注意が必要であり、黙って無視しないでください。
Phil Perry 14

1
@ iowatiger08:intのサイズは固定です。JVMに依存しませ。Javaはありません C.
マーティン・シュローダー

43

1024 * 1024 * 1024 * 1024また2147483648、Javaでは同じ値ではありません。

実際、Java では2147483648 IS N'T EVEN A VALUE(とはいえ2147483648L)です。コンパイラーは、それが何であるか、またはそれをどのように使用するかを文字通り知りません。だからそれは泣き言を言う。

1024はJavaの有効なintであり、有効なint値に別の有効な値を掛けた値intは常に有効ですint。計算がオーバーフローするため、直感的に期待できる値とは異なります。

次のコードサンプルを考えてみます。

public static void main(String[] args) {
    int a = 1024;
    int b = a * a * a * a;
}

これはコンパイルエラーを生成すると思いますか?もう少し滑りやすくなっています。
ループを3回繰り返し、ループで乗算するとどうなるでしょうか。

コンパイラーは最適化を許可されていますが、その間、プログラムの動作を変更することはできません。


このケースが実際にどのように処理されるかに関するいくつかの情報:

Javaおよび他の多くの言語では、整数は固定数のビットで構成されます。指定されたビット数に収まらない計算はオーバーフローします。計算は基本的にJavaで係数 2 ^ 32で実行されます。その後、値は符号付き整数に変換されます。

他の言語またはAPIは動的なビット数(BigIntegerJavaの場合)を使用し、例外を発生させるか、値をnot-a-numberなどのマジック値に設定します。


8
私にとって、「2147483648IS N'T EVEN A VALUE(aloughough 2147483648L)」という発言は、@ arshajiiが作ろうとしていたことを本当に強固にしました。
kdbanman 2014

あ、ごめんなさい、はい、それは私です。私はあなたの答えに概念オーバーフロー/モジュラー算術を欠いていた。編集内容に同意しない場合はロールバックできます。
Maarten Bodewes 2014

@owlsteadあなたの編集は実際に正しいです。それを含めなかった理由1024 * 1024 * 1024 * 1024は、処理方法に関係なく、書くことと同じではないことを強調したかったから2147473648です。言語が潜在的に対処できる方法はたくさんあります(いくつかリストアップしました)。合理的に分離されており、便利です。だから私はそれを残しておきます。人気のある質問に対してランクの高い回答がある場合、多くの情報がますます必要になります。
ランチャー

16

2番目のバリアントがエラーを生成しない理由はわかりません。

提案する動作、つまり、計算で整数に格納できる最大値よりも大きい値が生成された場合の診断メッセージの生成は、機能です。機能を使用するには、その機能を検討し、優れたアイデアであると考え、設計、指定、実装、テスト、文書化してユーザーに出荷する必要があります。

Javaの場合、リストにあるものの1つ以上が発生しなかったため、機能がありません。どちらかわかりません。Javaデザイナーに尋ねる必要があります。

C#の場合、これらすべてのことが起こりました(約14年前の現在)。したがって、C#の対応するプログラムはC#1.0以降エラーを生成しました。


45
これは役立つものを追加しません。Javaを試してもかまわないが、OPの質問にはまったく答えなかった。
セイリア2014

29
@Seiyria:元のポスターは「どうしてですか?」質問-「なぜ世界は私がそうあるべきだと思う方法ではないのですか?」実際のコードに関する正確な技術的な質問ではないため、これはStackOverflowにとって悪い質問です。あいまいで非技術的な質問に対する正解が曖昧で非技術的なものであるという事実は当然のことです。私は元のポスターにもっと良い質問をすることを勧め、「なぜしないのか」を避けます。質問。
Eric Lippert、2014

18
@Seiyria:私が指摘する受け入れられた回答も、このあいまいで非技術的な質問には回答しません。問題は、「なぜこれがエラーではないのか」です。受け入れられた答えは「合法だから」です。これは単に質問を言い直しているだけです。「なぜ空は緑ではないのですか」と答えます。「青いので」は質問に答えません。しかし、質問は悪い質問なので、私は回答者をまったく責めません。答えは貧しい質問に対する完全に合理的な答えです。
Eric Lippert、2014

13
エリックさん、これは私が投稿した質問です。そしてarshajiiの答えはまさに私が何をしたか(多分もっと)です。質問を正確に表現できないことがあります。Stackoverflowで投稿された質問をより正確に修正する人がいるのはそのためだと思います。「合法だから」という答えが欲しいのなら、この質問は投稿しません。「定期的な質問」を投稿するように最善を尽くしますが、私と同じように、学生でありプロではない人を理解してください。ありがとう。
WUJ 2014

5
@WUJこの回答IMHOは、追加の洞察と視点を提供します。すべての回答を読んだ後、私はこの回答が他の回答と同じくらい有効であることを発見しました。また、一部のソフトウェア製品の実装者は開発者だけではないという認識が高まります。
SoftwareCarpenter 14

12

arshajiiの回答に加えて、もう1つ説明したいことがあります。

エラーの原因は割り当てではなく、単にリテラルの使用です。あなたがしようとすると

long i = 2147483648;

右側がまだintリテラルで範囲外であるため、コンパイルエラーの原因にもなります。

したがって、int-values(および割り当てを含む)を使用した操作は、コンパイルエラーなし(および実行時エラーもなし)でオーバーフローする可能性がありますが、コンパイラーはこれらの大きすぎるリテラルを処理できません。


1
正しい。intをlongに割り当てると、暗黙のキャストが含まれます。しかし、値がキャストされる最初の場所のintとして存在することは決してありません:)
Cruncher

4

A:エラーではありません。

背景:乗算1024 * 1024 * 1024 * 1024によりオーバーフローが発生します。多くの場合、オーバーフローはバグです。オーバーフローが発生すると、プログラミング言語によって動作が異なります。たとえば、CおよびC ++では、符号なし整数の場合は「未定義の動作」と呼ばれ、動作は符号なし整数として定義されます(数学的結果を取りUINT_MAX + 1、結果が負であるUINT_MAX + 1限り加算し、結果がより大きい限り減算しますUINT_MAX)。

Javaの場合、int値のある操作の結果が許容範囲内にない場合、概念的にJavaは結果が許容範囲内になるまで2 ^ 32を加算または減算します。したがって、ステートメントは完全に合法であり、誤りではありません。それはあなたが望んでいたかもしれない結果を生み出しません。

この動作が役立つかどうか、コンパイラが警告を表示する必要があるかどうかは、確実に議論できます。個人的には警告は非常に役立つと思いますが、エラーは合法なJavaであるため正しくありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.