почему это выражение Java возвращает отрицательный результат? - PullRequest
1 голос
/ 20 декабря 2011
public static void main (String[] ses) {
    System.out.println(740 * (24 * 60 * 60 * 1000));
}

пробовал в гугле, он дал другой результат и на научном калькуляторе.

Ответы [ 5 ]

5 голосов
/ 20 декабря 2011

Потому что результат равен 63 936 000 000, что больше, чем вы можете сохранить в целочисленном Java (что по умолчанию таково, потому что ни один из них не имеет предваряющего L).Целое число может хранить только 4 байта (32 бита), и для этого числа потребуется 36 бит.Затем он переполняется, по существу, используя только последние (первые?) 32 бита результата.Поскольку первый (последний? Зависит от того, как вы на него смотрите) этих битов определяет, будет ли число подписано или нет, когда число снова фактически рассматривается как целое число, оно отображается как отрицательное.

Этоособенно полезен для вычисления хэшей, поскольку единственная логическая альтернатива, которую я вижу, состоит в том, чтобы сделать все числа сверх максимального значения равными максимальному значению, что, я думаю, мы можем согласиться, является плохим выбором.

Если вы сделали этоSystem.out.println(740L * (24 * 60 * 60 * 1000)); он должен использовать long, который может хранить намного большие числа.

Вы заметите, что у меня есть вопросительные знаки через это - байты - это байты, а биты - биты.Что действительно важно, так это то, как вы их интерпретируете.В сообществе разработчиков программного обеспечения существует спор о том, что на самом деле означает быть первой цифрой, битом или байтом.Рассмотрим «1234» как число.1 первая цифра?Большинство простых людей согласятся с этим, потому что это написано первым, когда мы смотрим на это.Но другие будут считать 4 первой цифрой, потому что это удобно делать в целях расчета.(Подумайте над тем, чтобы добавить - куда вы добавляете первым? Разве это не первое число?)

Так вот почему я, кажется, нерешительный, как я говорю первый / последний - я просто хочу прояснить, что происходитна.

2 голосов
/ 20 декабря 2011

все значения обрабатываются как тип java int, который обрабатывает 10 цифр, а ответ (63 936 000 000) содержит 11 цифр.Если вы преобразуете его в тип long, например

System.out.println( Long.valueOf(740) * (24 * 60 * 60 * 1000 ));

или

System.out.println( 740 * Long.valueOf((24 * 60 * 60 * 1000 )));

, вы получите правильный ответ.

1 голос
/ 20 декабря 2011

В спецификации языка Java в разделе 4.2.1 указан диапазон значений для целочисленных типов.

Вместо этого используйте этот расчет:

System.out.println(740 * 24 * 60 * 60 * 1000L);

Lобозначает, что 1000 является примитивом long (вы также можете использовать строчную букву L, но во многих шрифтах это слишком похоже на 1).Поскольку 1000 - это long, весь ответ становится long - это называется числовым повышением, в частности, расширяющимся преобразованием с int до long ( JLS 5.6 ).Примитив long обладает достаточной емкостью для правильного хранения результата.

То же самое с другими числовыми типами.Допустим, вам нужен процент, поэтому вы напишите свой расчет следующим образом:

int i = 99;
System.out.println(i / 100 * 100);

0 будет выведен на экран.Поскольку все используемые числа являются int примитивами, вы получите 99/100 = 0, потому что int не сохраняет часть справа от десятичной точки.

Если вы разделите i наa double со значением 100, вычисление будет работать - значение 100 помечается как double буквой 'd':

int i = 99;
System.out.println(i / 100d * 100);

..., что дает ожидаемый ответ 99,0.

'i' можно было бы сделать двойным вместо:

double i = 99;
System.out.println(i / 100 * 100);

... и ответ все равно будет правильным.

1 голос
/ 20 декабря 2011

В Java размер байта int равен 4. Ваш расчет превышает эти четыре байта.поэтому он выбрасывает некоторое отрицательное значение.

Это называется переполнением целого числа.

0 голосов
/ 20 декабря 2011

Когда вы просто записываете подобные вычисления, Java обрабатывает входные значения как целые числа и, таким образом, возвращает целое число.

(740 * (24 * 60 * 60 * 1000)) = 63 936 000 000

Поэтому он попытается сохранить 63936000000 в целом числе.Проблема в том, что целочисленный тип данных не предназначен для хранения чего-либо выше, чем 2 ^ 32 / 2-1 = 2 147 483 647.Это приводит к тому, что называется Integer Overflow .

. Поэтому, вместо того, чтобы Java угадывала ваш тип данных, вы можете вручную указать тип, который допускает большее число.В Учебное пособие по Java: примитивные типы данных вы можете увидеть список типов данных и их ограничения.Здесь вы заметите, что long будет соответствовать вашим потребностям, поскольку позволяет использовать до 2 ^ 64 различных значений вместо 2 ^ 32.

Так почему именно это число?

Еслизапустив программу, вы получите -488509440 .Это связано с тем, что, как уже упоминалось, целочисленный тип данных не позволяет хранить более 32 бит.Остальное обрезается.

Итак, двоичное представление 63936000000 равно 111011100010111000011111000000000000 .Для этого требуется 36 бит, поэтому первые четыре обрезаются.Это оставляет нам 11100010111000011111000000000000 , который в десятичном виде равен 3806457856 .

Целочисленный тип данных использует первую половину всех значений в 2 ^ 32 для отрицательных значений ивторая половина (минус 1, поскольку нам также нужно значение 0) для положительных значений (поэтому вы можете хранить только целые числа 2 ^ 32 / 2-1).

Итак, если мы вычтем32 ^ 2 от нашего номера, мы получим

3806457856 - 4294967296 = -488509440 .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...