なぜJavaは、10から99までのすべての数値の積が0であると考えているのですか?


131

次のコードブロックでは、出力が0になります。

public class HelloWorld{

    public static void main(String []args){
        int product = 1;
        for (int i = 10; i <= 99; i++) {
            product *= i;
        }
        System.out.println(product);
    }
}

なぜこれが起こるのか誰かが説明できますか?


106
おそらく整数オーバーフローがあります。
TheLostMind 2014年

68
製品の主要な要素を考慮すると、2約90回表示されます。つまり、ゼロ以外の出力を取得するには、少なくとも90ビットの変数が必要です。32と64はどちらも90未満です。ネイティブワードよりも大きい整数を計算するには、選択した言語で利用できる大きな整数クラスを使用する必要があります。
kasperd 2014年

62
技術的に、これは10から98までの数字の積である
AShelly

45
何?なぜ、この質問は、質問の重複として閉鎖されたの重複としてクローズされているこの疑問
Salman A

82
ガット99回の問題と2147483648 AINT 1
glenatron

回答:


425

プログラムが各ステップで行うことは次のとおりです。

          1 * 10 =          10
         10 * 11 =         110
        110 * 12 =        1320
       1320 * 13 =       17160
      17160 * 14 =      240240
     240240 * 15 =     3603600
    3603600 * 16 =    57657600
   57657600 * 17 =   980179200
  980179200 * 18 =   463356416
  463356416 * 19 =   213837312
  213837312 * 20 =   -18221056
  -18221056 * 21 =  -382642176
 -382642176 * 22 =   171806720
  171806720 * 23 =  -343412736
 -343412736 * 24 =   348028928
  348028928 * 25 =   110788608
  110788608 * 26 = -1414463488
-1414463488 * 27 =   464191488
  464191488 * 28 =   112459776
  112459776 * 29 = -1033633792
-1033633792 * 30 =  -944242688
 -944242688 * 31 =   793247744
  793247744 * 32 =  -385875968
 -385875968 * 33 =   150994944
  150994944 * 34 =   838860800
  838860800 * 35 =  -704643072
 -704643072 * 36 =   402653184
  402653184 * 37 =  2013265920
 2013265920 * 38 =  -805306368
 -805306368 * 39 = -1342177280
-1342177280 * 40 = -2147483648
-2147483648 * 41 = -2147483648
-2147483648 * 42 =           0
          0 * 43 =           0
          0 * 44 =           0
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
          0 * 97 =           0
          0 * 98 =           0

一部のステップでは、乗算の結果が小さい数(980179200 * 18 = 463356416)または不正な符号(213837312 * 20 = -18221056)になり、整数のオーバーフローがあったことを示しています。しかし、ゼロはどこから来るのでしょうか?読む。

intデータ型は32ビットの符号付き2の補数の整数であることを念頭に置いて、以下に各ステップの説明を示します。

Operation         Result(1)     Binary Representation(2)                                           Result(3)
----------------  ------------  -----------------------------------------------------------------  ------------
          1 * 10            10                                                               1010            10
         10 * 11           110                                                            1101110           110
        110 * 12          1320                                                        10100101000          1320
       1320 * 13         17160                                                    100001100001000         17160
      17160 * 14        240240                                                 111010101001110000        240240
     240240 * 15       3603600                                             1101101111110010010000       3603600
    3603600 * 16      57657600                                         11011011111100100100000000      57657600
   57657600 * 17     980179200                                     111010011011000101100100000000     980179200
  980179200 * 18   17643225600                               100 00011011100111100100001000000000     463356416
  463356416 * 19    8803771904                                10 00001100101111101110011000000000     213837312
  213837312 * 20    4276746240                                   11111110111010011111100000000000     -18221056
  -18221056 * 21    -382642176  11111111111111111111111111111111 11101001001100010101100000000000    -382642176
 -382642176 * 22   -8418127872  11111111111111111111111111111110 00001010001111011001000000000000     171806720
  171806720 * 23    3951554560                                   11101011100001111111000000000000    -343412736
 -343412736 * 24   -8241905664  11111111111111111111111111111110 00010100101111101000000000000000     348028928
  348028928 * 25    8700723200                                10 00000110100110101000000000000000     110788608
  110788608 * 26    2880503808                                   10101011101100010000000000000000   -1414463488
-1414463488 * 27  -38190514176  11111111111111111111111111110111 00011011101010110000000000000000     464191488
  464191488 * 28   12997361664                                11 00000110101101000000000000000000     112459776
  112459776 * 29    3261333504                                   11000010011001000000000000000000   -1033633792
-1033633792 * 30  -31009013760  11111111111111111111111111111000 11000111101110000000000000000000    -944242688
 -944242688 * 31  -29271523328  11111111111111111111111111111001 00101111010010000000000000000000     793247744
  793247744 * 32   25383927808                               101 11101001000000000000000000000000    -385875968
 -385875968 * 33  -12733906944  11111111111111111111111111111101 00001001000000000000000000000000     150994944
  150994944 * 34    5133828096                                 1 00110010000000000000000000000000     838860800
  838860800 * 35   29360128000                               110 11010110000000000000000000000000    -704643072
 -704643072 * 36  -25367150592  11111111111111111111111111111010 00011000000000000000000000000000     402653184
  402653184 * 37   14898167808                                11 01111000000000000000000000000000    2013265920
 2013265920 * 38   76504104960                             10001 11010000000000000000000000000000    -805306368
 -805306368 * 39  -31406948352  11111111111111111111111111111000 10110000000000000000000000000000   -1342177280
-1342177280 * 40  -53687091200  11111111111111111111111111110011 10000000000000000000000000000000   -2147483648
-2147483648 * 41  -88046829568  11111111111111111111111111101011 10000000000000000000000000000000   -2147483648
-2147483648 * 42  -90194313216  11111111111111111111111111101011 00000000000000000000000000000000             0
          0 * 43             0                                                                  0             0
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
          0 * 98             0                                                                  0             0
  1. 、正しい結果
  2. 結果の内部表現です(64ビットは説明のために使用されます)
  3. 下位32ビットの2の補数で表される結果です

ある数に偶数を掛けると、

  • ビットを左にシフトし、ゼロのビットを右に追加します
  • 結果は偶数になります

したがって、基本的にプログラムは偶数と別の数を繰り返し乗算し、結果のビットを右からゼロにリセットします。

PS:乗算に奇数のみが含まれる場合、結果はゼロにはなりません。


15
16進表記は、ここで起こっていることを理解するのに役立ちました。明確にしていただきありがとうございます!

1
ええ、長いリストの16進数の値も出力するようにプログラムを変更すると、より有益になります。
Hot Licks

4
この答えは正しいがありますので、多くの混乱が。最後の5行はその中心であり、それが実際にどのように機能するかを実際に示す場所はありません。(しかし、巨大なテーブルからそれを不可解にすることができます。)
Rex Kerr

6
別の言い方をすれば、2の係数を累積します。いくつかの数値は、12、16、20などの2の複数の係数を単独で与えます。2のすべての係数は、後続のすべての結果のすべてのビットを右シフトし、0のままにします。プレースホルダー。32回右シフトすると、32個のプレースホルダーゼロだけが残ります。
Keen

2
ベース10でも同様の効果が見られます。10で割り切れる数値を掛けるたびに、連続する整数のシリーズを掛けてみてください。製品の最後に少なくとも1つのゼロが追加され、そのゼロを削除することは不可能です整数の乗算による積から。ある時点で、すべてのn番目の最下位桁がゼロで埋められ、10 ** mを法として算術演算を行う場合(m番目の最下位桁以外のすべてを切り捨てる効果があります)、その後、ゼロになってしまいます。他の拠点と同様に。
リーライアン

70

コンピュータの乗算は実際には2 ^ 32を法として起こっています。被乗数で2のべき乗を十分に累積すると、すべての値は0になります。

ここでは、系列のすべての偶数と、数を割る2の最大累乗、および2の累乗

num   max2  total
10    2     1
12    4     3
14    2     4
16    16    8
18    2     9
20    4    11
22    2    12
24    8    15
26    2    16
28    4    18
30    2    19
32    32   24
34    2    25
36    4    27
38    2    28
40    8    31
42    2    32

42までの積は、x * 2 ^ 32 = 0(mod 2 ^ 32)に等しくなります。2の累乗のシーケンスは(特に)グレイコードに関連し、https://oeis.org/A001511として表示されます

編集:この質問に対する他の応答が不完全である理由を確認するには、オーバーフローがあっても、奇数の整数のみに制限された同じプログラムが0に収束しないという事実を考慮してください。


わーい!最後に、正解。人々はこの答えにもっと気づくはずです!
Rex Kerr、

これが唯一の正解です。他のすべては理由を説明しません。
OlivierGrégoire2014年

5
@OlivierGrégoire同意しない。受け入れられた答えは正しいと思い、完全に良い説明をします。これはもっと直接的です。
David Z

1
この答えをもっと多くの人に見てもらいたい。根本的な原因はここに記載されています!
lanpa 2014年

1
@DavidZ:そうですね。受け入れられた答えはほぼ正しいです-私の投稿は実際に私の冒頭文の「なぜ」に対処していません。しかし、受け入れ答えの近くには、「なぜゼロ」への答えに近いものですが、それは説明していない「なぜ42」 - 10と42の間のみ16偶数がある
user295691

34

整数オーバーフローのように見えます。

これをみて

BigDecimal product=new BigDecimal(1);
for(int i=10;i<99;i++){
    product=product.multiply(new BigDecimal(i));
}
System.out.println(product);

出力:

25977982938941930515945176761070443325092850981258133993315252362474391176210383043658995147728530422794328291965962468114563072000000000000000000000

出力はint値ではなくなりました。次に、オーバーフローのために誤った値を取得します。

オーバーフローすると、最小値に戻り、そこから継続します。アンダーフローすると、最大値に戻り、そこから継続します。

詳しい情報

編集

次のようにコードを変更してみましょう

int product = 1;
for (int i = 10; i < 99; i++) {
   product *= i;
   System.out.println(product);
}

出力:

10
110
1320
17160
240240
3603600
57657600
980179200
463356416
213837312
-18221056
-382642176
171806720
-343412736
348028928
110788608
-1414463488
464191488
112459776
-1033633792
-944242688
793247744
-385875968
150994944
838860800
-704643072
402653184
2013265920
-805306368
-1342177280
-2147483648
-2147483648>>>binary representation is 11111111111111111111111111101011 10000000000000000000000000000000 
 0 >>> here binary representation will become 11111111111111111111111111101011 00000000000000000000000000000000 
 ----
 0

22

整数オーバーフローが原因です。多くの偶数を一緒に乗算すると、2進数は多くの後続ゼロを取得します。に32を超える後続ゼロがあるint場合、それはにロールオーバーし0ます。

これを視覚化するために、オーバーフローしない数値タイプで計算された16進数の乗算を以下に示します。末尾のゼロがゆっくりと成長する様子intを確認し、最後の8桁の16進数で構成されることに注意してください。42(0x2A)を掛けると、32ビットすべてintがゼロになります。

                                     1 (int: 00000001) * 0A =
                                     A (int: 0000000A) * 0B =
                                    6E (int: 0000006E) * 0C =
                                   528 (int: 00000528) * 0D =
                                  4308 (int: 00004308) * 0E =
                                 3AA70 (int: 0003AA70) * 0F =
                                36FC90 (int: 0036FC90) * 10 =
                               36FC900 (int: 036FC900) * 11 =
                              3A6C5900 (int: 3A6C5900) * 12 =
                             41B9E4200 (int: 1B9E4200) * 13 =
                            4E0CBEE600 (int: 0CBEE600) * 14 =
                           618FEE9F800 (int: FEE9F800) * 15 =
                          800CE9315800 (int: E9315800) * 16 =
                         B011C0A3D9000 (int: 0A3D9000) * 17 =
                        FD1984EB87F000 (int: EB87F000) * 18 =
                      17BA647614BE8000 (int: 14BE8000) * 19 =
                     25133CF88069A8000 (int: 069A8000) * 1A =
                    3C3F4313D0ABB10000 (int: ABB10000) * 1B =
                   65AAC1317021BAB0000 (int: 1BAB0000) * 1C =
                  B1EAD216843B06B40000 (int: 06B40000) * 1D =
                142799CC8CFAAFC2640000 (int: C2640000) * 1E =
               25CA405F8856098C7B80000 (int: C7B80000) * 1F =
              4937DCB91826B2802F480000 (int: 2F480000) * 20 =
             926FB972304D65005E9000000 (int: E9000000) * 21 =
           12E066E7B839FA050C309000000 (int: 09000000) * 22 =
          281CDAAC677B334AB9E732000000 (int: 32000000) * 23 =
         57BF1E59225D803376A9BD6000000 (int: D6000000) * 24 =
        C56E04488D526073CAFDEA18000000 (int: 18000000) * 25 =
      1C88E69E7C6CE7F0BC56B2D578000000 (int: 78000000) * 26 =
     43C523B86782A6DBBF4DE8BAFD0000000 (int: D0000000) * 27 =
    A53087117C4E76B7A24DE747C8B0000000 (int: B0000000) * 28 =
  19CF951ABB6C428CB15C2C23375B80000000 (int: 80000000) * 29 =
 4223EE1480456A88867C311A3DDA780000000 (int: 80000000) * 2A =
AD9E50F5D0B637A6610600E4E25D7B00000000 (int: 00000000)

1
これは少し誤解を招きやすいです。ゼロになる理由を正しく示していますが、各値は乗算後に32ビットのintに保持されるため、各ステップの後に切り捨てられる必要があります。回答の書き方は、forループが終了するまで切り捨てられないことを意味します。各ステップで最後の8桁のみを表示した方がよいでしょう。
RyNo 2014年

@KamikazeScotsman私はあなたの提案に基づいて私の答えを改善しました。冗長ゼロが少なくなり、32ビットintの可視性が向上します。
Tim S.

1
+1は、各ステージでの実際の値と32ビット値を示し、値が切り捨てられていることを強調します...
kwah

14

真ん中のどこかで0製品として入手します。したがって、製品全体が0になります。

あなたの場合:

for (int i = 10; i < 99; i++) {
    if (product < Integer.MAX_VALUE)
        System.out.println(product);
    product *= i;
}
// System.out.println(product);

System.out.println(-2147483648 * EvenValueOfi); // --> this is the culprit (Credits : Kocko's answer )

O/P :
1
10
110
1320
17160
240240
3603600
57657600
980179200
463356416
213837312
-18221056
-382642176
171806720
-343412736
348028928
110788608
-1414463488
464191488
112459776
-1033633792
-944242688
793247744
-385875968
150994944
838860800
-704643072
402653184
2013265920
-805306368
-1342177280  --> Multiplying this and the current value of `i` will also give -2147483648 (INT overflow)
-2147483648  --> Multiplying this and the current value of `i` will also give -2147483648 (INT overflow)

-2147483648  ->  Multiplying this and the current value of 'i' will give 0 (INT overflow)
0
0
0

現在の値にi0出力として取得した数値を掛けるたびに。


@KickButtowski-数値を乗算します。理由はわかります:P
TheLostMind 2014年

@KickButtowski-0に他の数値を掛けると、オーバーフローがいずれかの時点で0を返した後、常に0になります。
Moose氏2014年

私はそうしましたが、他の人も学ぶことができるように、あなたはもっと情報を提供するべきだと思います
キック・バットウスキー2014年

@KickButtowski-回答を更新しました。OP部分を確認してください。
TheLostMind 2014年

8
@KickButtowski:オーバーフローの折り返しが2のべき乗で行われるためです。基本的に、OPは2 ^ 32を法とする{10 x 11 x 12 x ... x 98}を計算しています。その積では2の倍数が32回を超えて出現するため、結果はゼロです。
ruakh 2014年

12

既存の回答の多くはJavaの実装の詳細とデバッグ出力を示しているため、バイナリ乗算の背後にある計算を見て、その理由を実際に確認してみましょう。

@kasperdのコメントは正しい方向に進んでいます。数値を直接乗算するのではなく、その数値の素因数を乗算するとします。多くの数値よりも2が素因数になります。バイナリでは、これは左シフトと同じです。可換性により、最初に2の素因数を掛けることができます。つまり、左シフトを行うだけです。

2項乗算ルールを見ると、1が特定の桁位置になるのは、両方のオペランド値が1の場合のみです。

したがって、左シフトの効果は、結果をさらに乗算するときに1の最下位ビット位置が増加することです。

integerには最下位ビットのみが含まれるため、結果で素因数2が十分に頻繁に作成される場合、それらはすべて0に設定されます。

この分析では、2の補数表現は重要ではありません。乗算結果の符号は、結果の数値とは無関係に計算できるためです。つまり、値がオーバーフローして負になると、最下位ビットは1として表されますが、乗算中は再び0として扱われます。


7

このコードを実行すると、何が得られますか-

          1 * 10 =          10
         10 * 11 =         110
        110 * 12 =        1320
       1320 * 13 =       17160
      17160 * 14 =      240240
     240240 * 15 =     3603600
    3603600 * 16 =    57657600
   57657600 * 17 =   980179200
  980179200 * 18 =   463356416 <- Integer Overflow (17643225600)
  463356416 * 19 =   213837312
  213837312 * 20 =   -18221056
  -18221056 * 21 =  -382642176
 -382642176 * 22 =   171806720
  171806720 * 23 =  -343412736
 -343412736 * 24 =   348028928
  348028928 * 25 =   110788608
  110788608 * 26 = -1414463488
-1414463488 * 27 =   464191488
  464191488 * 28 =   112459776
  112459776 * 29 = -1033633792
-1033633792 * 30 =  -944242688
 -944242688 * 31 =   793247744
  793247744 * 32 =  -385875968
 -385875968 * 33 =   150994944
  150994944 * 34 =   838860800
  838860800 * 35 =  -704643072
 -704643072 * 36 =   402653184
  402653184 * 37 =  2013265920
 2013265920 * 38 =  -805306368
 -805306368 * 39 = -1342177280
-1342177280 * 40 = -2147483648
-2147483648 * 41 = -2147483648
-2147483648 * 42 =           0 <- produce 0 
          0 * 43 =           0

整数オーバーフローの原因-

980179200 * 18 =   463356416 (should be 17643225600)

17643225600 : 10000011011100111100100001000000000 <-Actual
MAX_Integer :     1111111111111111111111111111111
463356416   :     0011011100111100100001000000000 <- 32 bit Integer

原因0を生成-

-2147483648 * 42 =           0 (should be -90194313216)

-90194313216: 1010100000000000000000000000000000000 <- Actual
MAX_Integer :       1111111111111111111111111111111
0           :      00000000000000000000000000000000 <- 32 bit Integer

6

最終的に、計算はオーバーフローし、最終的にはそのオーバーフローがゼロの積になります。ときそれが起こるproduct == -2147483648i == 42。このコードを試して、自分で確認してください(またはここでコードを実行してください)。

import java.math.BigInteger;

class Ideone {
    public static void main (String[] args) throws java.lang.Exception {
        System.out.println("Result: " + (-2147483648 * 42));
    }
}

ゼロになると、もちろんゼロのままです。以下は、より正確な結果を生成するコードです(ここでコードを実行できます)。

import java.math.BigInteger;

class Ideone {
    public static void main (String[] args) throws java.lang.Exception {
        BigInteger p = BigInteger.valueOf(1);
        BigInteger start = BigInteger.valueOf(10);
        BigInteger end = BigInteger.valueOf(99);
        for(BigInteger i = start; i.compareTo(end) < 0; i = i.add(BigInteger.ONE)){
            p = p.multiply(i);
            System.out.println("p: " + p);
        }
        System.out.println("\nProduct: " + p);
    }
}

<F(18)19で、それはF(19)ので、オーバフロー既にある-それは井戸第42回反復する前に(単語の正確な意味での)オーバーフロー
user295691

はい。ただし、オーバーフローは、42回目の反復までゼロの結果につながらないか、その結果にはなりません。
Trevor、

私が得ているのは、「なぜ」に対応していないことだと思います。なぜ累積積が0を通過するのでしょうか。Tim S.の答えはその理由を示していますが、実際の答えはモジュラー演算にあります。
user295691 2014年

問題は、製品がゼロを通過する理由を問うものではありません。コードがゼロを生成する理由を尋ねます。言い換えれば、OPはモジュラー算術よりもJava言語のダイナミクスに関心があると思いますが、おそらく私は間違っています。私が誰かの質問を誤解したのはこれが初めてではありません。
Trevor、

たとえば、このプログラムが11から99までのすべての奇数の積を取った場合、ゼロに到達しません。あなたの答えは、なぜこれが起こるのかについては本当に触れていません。
user295691 2014年

1

整数オーバーフローです。

intデータ型は4バイト、つまり32ビットです。したがって、2 ^(32-1)-1(2,147,483,647)より大きい数値は、このデータ型に格納できません。数値が不正になります。

非常に大きな数の場合、クラスをインポートして使用する必要があります java.math.BigInteger:

BigInteger product = BigInteger.ONE;
for (long i = 10; i < 99; i++) 
    product = product.multiply(BigInteger.valueOf(i));
System.out.println(product.toString());

注:intデータ型にはまだ大きすぎるが、8バイト以内に収まるほど小さい数値(絶対値が2 ^(64-1)-1以下)の場合、longプリミティブを使用する必要があります。

アルゴリズムの練習セクション(https://www.hackerrank.com/domains/algorithms/warmup)などのHackerRankの練習問題(www.hackerrank.com)には、非常に優れた多数の質問が含まれているため、使用する適切なデータ型について考えます。

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