Weird Result Incrementing a Short Beyond its Maxim

2019-07-05 19:38发布

问题:

When i execute this code it gives value of s as -7616. Why so? Is this because loss of data while converting it to short from int or something else?

public static void main(String[] args) {
        // TODO code application logic here
       short s=0;
       int x=123456;
       int i=8;
       s +=x;
       System.out.println(s);

    }

回答1:

Good question! It made me think about things I haven't thought about in a long while and I had to brush up on a couple of concepts. Thanks for helping me knock the rust off my brain.

For me this type of question is best visualized in binary (for reasons that will quickly become apparent):

Your original number (forgive the leading zeroes; I like groups of 4):

0001 1110 0010 0100 0000

A short, however, is a 16-bit signed two's complement integer according to the Java Language Specification (JLS) section 4.2. Assigning the integer value 123456 to a short is known as a "narrowing primitive conversion" which is covered in JLS 5.1.3. Specifically, a "narrowing conversion of a signed integer to an integral type T simply discards all but the n lowest order bits, where n is the number of bits used to represent type T."

Discarding all but the lowest 16 bits leaves us with:

1110 0010 0100 0000

In an unsigned integer this value is 57,290, however the short integer is a signed two's complement integer. The 1 in the leftmost digit indicates a negative number; to get the value of the number you must invert the bits and add 1:

Original:

1110 0010 0100 0000

Invert the bits:

0001 1101 1011 1111

Add 1:

0001 1101 1100 0000

Convert that to decimal and add the negative sign to get -7,616.

Thanks again for asking the question. It's okay to not know something so keep asking and learning. I had fun answering...I like diving into the JLS, crazy, I know!



回答2:

You are simply overflowing the maximum value of a short :

short: The short data type is a 16-bit signed two's complement integer. It has a minimum value of -32,768 and a maximum value of 32,767 (inclusive). As with byte, the same guidelines apply: you can use a short to save memory in large arrays, in situations where the memory savings actually matters.

What happens when there is such overflow is equivalent to this algorithm :

/** Returns an integer which is equal to the short obtained by the ((short) n) conversion */
public static int int2short(int n) {
    int sign      = n > 0 ? 1 : -1;
    int increment = sign * (Short.MAX_VALUE - Short.MIN_VALUE + 1);
    for ( ; n > Short.MAX_VALUE || n < Short.MIN_VALUE ; n -= increment);
    return n;
}


回答3:

Incrementing a short beyond its maximum value is called overflow. When overflow occurs, the value becomes the minimum value of the type, and start counting up again.

So here's how you got -7616 from trying to store 0+123456 in a short:

0 --> 32767

-32768 --> 32767

-32768 --> -7616

In other words,

32768+ 32768+ 32768+ (32768 -7616) = 123456


回答4:

The compound assignment operator += (all of them really) cast their result

A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T) ((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.

So

s += x;

becomes

s = (short) (s + x);

Now, because s is a short and x is an int, binary numeric promotion is performed on the short value before the addition is applied. The short value is converted to an int (which is not a problem).

s = (short) (8 + 123456)
s = (short) (123464)

The cast applies a narrowing primitive conversion which

[...] may lose information about the overall magnitude of a numeric value and may also lose precision and range.

That's what happens

s = -7608