Это классическая проблема из очень хорошей книги Java Puzzlers Ссылка на книгу
Головоломка 3: Длинное деление Эта головоломка называется Длинное деление, потому что онакасается программы, которая делит два длинных значения.Дивиденд представляет количество микросекунд в день;делитель, количество миллисекунд в день.Что печатает программа?
public class LongDivision {
public static void main(String[] args) {
final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
}
}
Решение 3: Длинное деление Эта головоломка кажется достаточно простой.Число миллисекунд в день и количество микросекунд в день являются константами.Для наглядности они выражены в виде продуктов.Количество микросекунд в день: (24 часа / день · 60 минут / час · 60 секунд / минута · 1000 миллисекунд / секунда · 1000 микросекунд / миллисекунда).
Количество миллисекунд в день отличается только тем, что в нем отсутствует конечный коэффициент 1000.Когда вы делите число микросекунд в день на количество миллисекунд в день, все факторы в делителе аннулируются, и у вас остается 1000, то есть число микросекунд в миллисекунде.И делитель, и делитель имеют тип long, который достаточно велик, чтобы вместить любой продукт без переполнения.
Похоже, что программа должна вывести 1000. К сожалению, она печатает 5. Что именноздесь происходит?Проблема в том, что вычисление константы MICROS_PER_DAY переполнено.Несмотря на то, что результат вычислений умещается долго, он не помещается в int.Вычисление выполняется полностью в арифметике, и только после того, как вычисление завершено, результат переводится в long.К тому времени уже слишком поздно: вычисления уже переполнились, возвращая слишком низкое значение с коэффициентом 200. Переход от int к long - это расширение примитивного преобразования [JLS 5.1.2], которое сохраняет (неверно)численная величина.Затем это значение делится на MILLIS_PER_DAY, который был вычислен правильно, потому что он помещается в int.Результатом этого деления является 5. Так почему же вычисления выполняются в арифметике?Потому что все факторы, которые умножаются вместе, являются значениями типа int.Когда вы умножаете два значения типа int, вы получаете другое значение типа int.Java не имеет целевой типизации, языковая особенность, в которой тип переменной, в которой должен быть сохранен результат, влияет на тип вычисления.Программу легко исправить, используя длинный литерал вместо int в качестве первого фактора в каждом продукте.Это заставляет все последующие вычисления в выражении выполняться с длинной арифметикой.Хотя это необходимо делать только в выражении для MICROS_PER_DAY, это хорошая форма для обоих продуктов.Точно так же не всегда необходимо использовать long в качестве первого значения в продукте, но это хорошая форма для этого.Начало обоих вычислений с длинными значениями дает понять, что они не будут переполнены.
Эта программа печатает 1000, как и ожидалось:
public class LongDivision {
public static void main(String[] args) {
final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24L * 60 * 60 * 1000;
System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
}
}
Урок прост: при работе с большими числами следите за переполнением.это тихий убийца.Тот факт, что переменная достаточно велика для хранения результата, не означает, что вычисление, приводящее к результату, имеет правильный тип.Если вы сомневаетесь, выполните все вычисления, используя длинную арифметику.Урок для языковых дизайнеров заключается в том, что, возможно, стоит уменьшить вероятность тихого переполнения.Это может быть сделано путем предоставления поддержки арифметики, которая не переполняется молча.Программы могут генерировать исключение вместо переполнения, как это делает Ada, или они могут автоматически переключаться на большее внутреннее представление по мере необходимости, чтобы избежать переполнения, как это делает Lisp.Оба этих подхода могут иметь связанные с ними потери производительности.Другой способ уменьшить вероятность молчаливого переполнения - это поддержка целевой типизации, но это значительно усложняет систему типов [Modula-3 1.4.8].