2013-12-06 1 views
5

Может кто-нибудь объяснить мне, как будто мне пять, почему я получаю различное поведение для двух из четырех примитивных типов, представляющих целые числа в Java? AFAIK все четыре подписываются, и все они используют самый старший бит в качестве знакового бита, так почему байты и короткие вести себя нормально, а int и long действуют, ну, странно? Фрагмент документа оракула, объясняющий это, был бы совершенным.Непоследовательное поведение примитивных целочисленных типов в Java

byte a = (byte) (Math.pow(2, 7)-1); //127 - as expected 
short b = (short) (Math.pow(2, 15)-1); //32767 - as expected 
int c = (int) (Math.pow(2, 31)-1); //2147483647 - as expected 
long d = (long) (Math.pow(2, 63)-1); //9223372036854775807 - as expected 

a = (byte) (Math.pow(2, 7)); //-128 - as expected 
b = (short) (Math.pow(2, 15)); //-32768 - as expected 
c = (int) (Math.pow(2, 31)); //2147483647 - why not '-2147483648'? 
d = (long) (Math.pow(2, 63)); //9223372036854775807 - why not '-9223372036854775808'? 

a = (byte) (Math.pow(2, 8)); //0 - as expected 
b = (short) (Math.pow(2, 16)); //0 - as expected 
c = (int) (Math.pow(2, 32)); //2147483647 - why not '0'? 
d = (long) (Math.pow(2, 64)); //9223372036854775807 - why not '0'? 

Я использую Oracle SE SE 1.7 для Windows. ОС Windows 7 Professional SP1

java version "1.7.0_45" 
Java(TM) SE Runtime Environment (build 1.7.0_45-b18) 
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode) 

EDIT, после прочтения всех ответов и настраивая свой код.
Итак, чтобы подвести итог, единственный способ получить ожидаемые значения - это использование BigInteger. Оператор Shift работает хорошо для байтов, шорт и ints, но когда дело доходит до longs, я купил его на одной неисправности.

byte a = (byte) ((1l << 7) - 1); //127 - as expected 
short b = (short) ((1l << 15) - 1); //32767 - as expected 
int c = (int) (1l << 31) - 1; //2147483647 - as expected 
long d = (1l << 63) - 1; //9223372036854775807 - as expected 

a = (byte) (1l << 7); //-128 - as expected 
b = (short) (1l << 15); //-32768 - as expected 
c = (int) 1l << 31; //-2147483648 - as expected 
d = 1l << 63; //-9223372036854775808 - as expected 

a = (byte) (1l << 8); //0 - as expected 
b = (short) (1l << 16); //0 - as expected 
c = (int) (1l << 32); //0 - as expected 
d = 1l << 64; //1 instead of 0, probably because of the word length limitation  

С BigInteger все работает безупречно

byte a = (byte) (new BigInteger("2").pow(7).longValue() - 1); //127 - as expected 
short b = (short) (new BigInteger("2").pow(15).longValue() - 1); //32767 - as expected 
int c = (int) (new BigInteger("2").pow(31).longValue() - 1); //2147483647 - as expected 
long d = (new BigInteger("2").pow(63).longValue() - 1); //9223372036854775807 - as expected 

a = (byte) (new BigInteger("2").pow(7).longValue()); //-128 - as expected 
b = (short) (new BigInteger("2").pow(15).longValue()); //-32768 - as expected 
c = (int) new BigInteger("2").pow(31).longValue(); //-2147483648 - as expected 
d = new BigInteger("2").pow(63).longValue(); //-9223372036854775808 - as expected 

a = (byte) (new BigInteger("2").pow(8).longValue()); //0 - as expected 
b = (short) (new BigInteger("2").pow(16).longValue()); //0 - as expected 
c = (int) (new BigInteger("2").pow(32).longValue()); //0 - as expected 
d = new BigInteger("2").pow(64).longValue(); //0 - as expected 

Спасибо всем за большую помощь!

+1

попробуйте 2 << 7, 2 << 15, ... вместо этого, точнее, – hoaz

+0

Хороший совет.Спасибо – Moyshe

ответ

6

Section 5.1.3 of the JLS говорит о поведении сужающегося примитивного преобразования используется литым

В противном случае, один из следующих двух случаев должна быть правда:

Значение должно быть слишком маленьким (отрицательное значение большой величины или отрицательной бесконечности), а результатом первого шага является наименьшее представимое значение типа int или long.

Значение должно быть слишком большим (положительное значение большой величины или положительной бесконечности), а результатом первого шага является наибольшее представимое значение типа int или long.

(курсив мой)

Именно поэтому (int) (Math.pow(2, 32)); становится Integer.MAX_VALUE и (long) (Math.pow(2, 64)) становится Long.MAX_VALUE.

+0

Обратите внимание, что в этом разделе речь идет о плавающей запятой к целому и между целыми типами. –

+0

Ответ идет к вам, поскольку вы предоставили документы оракула, я попросил – Moyshe

3

Math.pow() возвращает double, который затем округляется при преобразовании в интегральные типы. Окно double, очевидно, округлоло волосы под точностью, необходимой для их переполнения.

+0

Ну, хорошая работа за то, чтобы быть быстрее с ответом :-) – Moyshe

2

Поучительный:

public class PowTest { 
    public static void main(String[] argv) { 
     double powResult = Math.pow(2.0,31.0); 
     int powInt = (int) powResult; 
     long powLong = (long) powResult; 
     int longInt = (int) powLong; 
     System.out.println("Double = " + powResult + ", int = " + powInt + ", long = " + powLong + ", longInt = " + longInt); 
    } 
} 

Результат:

C:\JavaTools>java PowTest 
Double = 2.147483648E9, int = 2147483647, long = 2147483648, longInt = -2147483648 

Двойной -> Преобразование INT округляется. Преобразование long -> int усекается.

+0

Это интересно – Moyshe

+0

+1: Прочитав выдержку из JLS * rgettman *, я думал о точно таком же тестовом случае :) –

+0

Собственно, если я понимаю правильно, вот почему я получил отрицательные числа в байтах и ​​короткие. Двойной округлен до int, а затем усечен до байта и короткий, не так ли? – Moyshe