Как рассчитать вывод значения переполнения - PullRequest
0 голосов
/ 09 мая 2019

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

Например, следующая программа должна вывести 1000 в качестве выходных данных, но выдает 5 из-за переполнения целых чисел.

public class IntegerOvewflow {

    /**
     * Java does not have target typing, a language feature wherein the type of the
     * variable in which a result is to be stored influences the type of the
     * computation.
     * 
     * @param args
     */
    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 ]

1 голос
/ 09 мая 2019

Укажите, что они long с L, потому что если вы не выполняете int умножение, которое приводит к int, который затрагивает переполнение, а затем сохраняется в long

public static void main(String[] args) {
    final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000L;
    final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000L;
    System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);   // 1000
}

Выезд: https://ideone.com/5vHjnH

1 голос
/ 09 мая 2019

Это классическая проблема из очень хорошей книги 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].

0 голосов
/ 09 мая 2019

Вы можете использовать BigInteger класс именно для этих целей.

Документация Oracle: здесь

В этой ссылке есть хорошая быстрая статья

...