4

I’m seeing some strange behavior multiplying integers with Java. I was doing some coding exercises and came upon the following fizz buzz type of exercise. The requirements: Given an integer, write a function that finds the product of every multiple of 3 which is smaller than the given integer, except any multiple of 5. Eg, given 17 we want to return 12*9*6*3 (= 1944). I wrote the following:

public int findProduct(int limit) { 
    int product = 1;
    for(int n = 3; n < limit; n = n + 3) {
        if (n % 5 != 0) {
            product = product * n;
        }
    }
    return product;
}

This works just fine for small numbers. However in testing I found that once you get above 33 somehow the return value is way off. For example if I call the function with a limit of 36, it returns -1.466221696E9. This is where I get confused. I am multiplying positive integers and the result is somehow negative.

However, I discovered that if you declare a double it seems to always return the correct result.

public double findProduct(int limit) { 
    double product = 1;
    for(int n = 3; n < limit; n = n + 3) {
        if (n % 5 != 0) {
            product = product * n;
        }
    }
    return product;
}

My question is: Why is this happening with the integers and what is different about the double type that makes it perform correctly?

marstran
  • 26,413
  • 5
  • 61
  • 67
C. Peck
  • 3,641
  • 3
  • 19
  • 36

4 Answers4

5

Let's examine this by taking an example of Integer.

Integer.MAX_VALUE can be represented as 01111111111111111111111111111111 which is a 32 bit long string(including sign bit). Now if you happen to add 1 to the above string, it results in 10000000000000000000000000000000 which is same as Integer.MIN_VALUE. This is called overflow of Integer.

System.out.println(Integer.toBinaryString(Integer.MAX_VALUE));
// 1111111111111111111111111111111

According to Integer#toBinaryString:

The unsigned integer value is the argument plus 232 if the argument is negative; otherwise it is equal to the argument. This value is converted to a string of ASCII digits in binary (base 2) with no extra leading 0s.

So that's why you can't see the sign bit but the real value of Integer.MAX_VALUE is 01111111111111111111111111111111. Now take a look at this code:

System.out.println(Integer.toBinaryString(Integer.MAX_VALUE + 1));
// 10000000000000000000000000000000
System.out.println(Integer.toBinaryString(Integer.MIN_VALUE));
// 10000000000000000000000000000000

The output of both the numbers is same. Java doesn't protect against Integer overflow. It is the developer who should take care of this. So what could be the possible solution to this problem? You can use other data types such as long or BigInteger. Here are the max values you might be interested in:

System.out.println(Integer.MAX_VALUE); // 2147483647
System.out.println(Long.MAX_VALUE); // 9223372036854775807
System.out.println(Double.MAX_VALUE); // 1.7976931348623157E308
System.out.println(Float.MAX_VALUE); // 3.4028235E38

Once the Integer reaches MAX_VALUE it will start to overflow and you will end up in negative value.

Aniket Sahrawat
  • 12,410
  • 3
  • 41
  • 67
2

This is called integer overflow. Essentially, your numbers become too large for the data type that you're storing the solution in. When this happens, the numbers wrap around to negative numbers because of twos-complement. A double is a larger data type but if you go large enough, it too will become negative.

See these articles on two's complement and integer overflow. They'll thoroughly explain it.

Edit: A simplified explanation from the wiki article. I still recommend you read this. Suppose we had a 3-bit architecture (000 through 111). In two's complement, we say that the leading bit determines whether the number is positive/negative. If it's negative, then the following digits add positively to the negative number. Here's a simple counting example:

+---------+--------+
| Decimal | Binary |
+---------+--------+
|       0 |    000 |
|       1 |    001 |
|       2 |    010 |
|       3 |    011 |
|      -4 |    100 | <----- note this isn't negative 0, or +4.
|      -3 |    101 | <----- note this isn't -1, or +5
|      -2 |    110 | <----- note this isn't +6
|      -1 |    111 |<----- note this isn't -3, or +7
|       0 |    000 |
+---------+--------+

And the pattern repeats...

So for a bigger system, the same rule applies (32bit, or 64bit). The numbers will wrap around at 2^(n-1) where n represents how many bits the number has (e.g. for a 3-bit system as shown above, it wraps at 2^(3-1) = 2^2 = 4). Note that using two's complement means, that the range of positive space is halved. For a 3-bit unsigned integer, normally you'd be able to count from 0 to 7, now you can only count between -4 and 3 (still 8 numbers total).

Now try multiplying two positive numbers: +2 * +3 = ?

Normally this would be +6, or 110. But in two's complement, this is actually -2.

jsonV
  • 1,650
  • 2
  • 9
  • 23
  • Can you please explain "the numbers wrap around to negative numbers because of twos-complement" ? – Wijay Sharma Jan 29 '20 at 09:39
  • I edited my explanation to include an example with a small counting system, hopefully that explanation is thorough enough. – jsonV May 13 '20 at 15:43
2

If you wanted to completely avoid the integer overflow problem, try using BigInteger:
https://docs.oracle.com/javase/7/docs/api/java/math/BigInteger.html
This allows arbitrary length integers.

(Actually from looking at the source code for BigInteger, it seems there is a maximum size, i.e. the int[] which stores the BigInteger of:

Integer.MAX_VALUE / Integer.SIZE + 1

Much bigger than anyone could reasonably want!)

Bill Naylor
  • 482
  • 1
  • 9
  • 14
  • Another nice answer, and surprise, you are now able to post comments. Beyond that, a quick note: focus on quality, not quantity ;-) – GhostCat Mar 24 '19 at 18:38
1

exceed the range of Double.MAX_VALUE thats why result is coming negative

example:

for four digit binary data is.

00001 here left most bit reserved for sign bit, [0 means +, 1 means -], and rest of 4 bits are used for storing value. so 00001 binary= decimal +1

so for 4 bit we will be able to write maximum 15 decimal value.

Now if I want to write 16 then overflowing the value range.

so if I convert the decimal 16 to binary data, it will be 10000, so sign bit is 1 now. and final result will be negative

Mahamudul Hasan
  • 2,745
  • 2
  • 17
  • 26