Java MOD оператор возвращает отрицательное значение - PullRequest
0 голосов
/ 12 ноября 2018

У меня есть этот метод:

private static int generateNo(int randomNo, int value){
    return   ((randomNo*value)%256);
}

в моем примере randomNo = 17719 qValue = 197920

Когда я вычисляю это с помощью калькулятора, возвращаемое значение должно быть 224, однако, когда я запускаю программу, она возвращает -32.

Может кто-нибудь объяснить, пожалуйста.

Ответы [ 4 ]

0 голосов
/ 12 ноября 2018

В Java используется подписанный остаток вместо операции, которая обычно подразумевается как модуль (неотрицательный остаток евклидова деления). К счастью, для степеней двойки это действительно легко исправить: использовать побитовый &. В любом случае, об этом легче думать, поскольку это тривиальная операция над битами, а не результат сложного алгоритма деления.

Например:

private static int generateNo(int randomNo, int value) {
    return randomNo * value & 255;
}

Это не может иметь отрицательный результат, поскольку & 255 гарантирует, что могут быть установлены только младшие 8 битов результата, поэтому результат наверняка находится в диапазоне [0..255].

Разрешение переноса умножения в порядке, если вам нужны некоторые младшие биты результата, как здесь (младшие 8). Он не работает должным образом, если вы хотите вычислить (x * y) MOD p, где p не является степенью двойки, потому что тогда (после обхода остатка со знаком Java) фактические вычисления становятся (из-за переноса) ((x * y) MOD 2³²) MOD p. IFF p делит 2³² (т. Е. Если p - это степень двух, не превышающая 2³²) , то , что упрощается до (x * y) MOD p.

Или с более точным представлением: биты продукта - это младшие 32 бита «полного» продукта (полное произведение двух 32-битных целых имеет 64 бита), конечно, если нам нужны только эти биты (или некоторые их подмножества, такие как самые низкие 8), тогда это нормально. Но если желаемый результат будет зависеть от 32 старших бит продукта, то, очевидно, нам нужно будет вычислить эти биты. (x * y) MOD p где p не является степенью двойки, зависит от всех битов полного произведения.

0 голосов
/ 12 ноября 2018

Небольшой намек. Если у вас есть неожиданное отрицательное значение при умножении (или сумме) чисел, в основном это переполнение чисел:

private static int generateNo(int randomNo, int value) {
    return (int)(((long)randomNo * value) % 256);
}
0 голосов
/ 12 ноября 2018

Здесь есть две вещи.

  1. Умножение двух int вместе может привести к числу, большему Integer.MAX_VALUE (2147483647), что обернется до отрицательного числа.
  2. Применение оператора модуля к положительному и отрицательному числу приводит к отрицательному числу.

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

Что должен, например, generateNo(-100, 50) произвести?

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

Math.abs(randomNo * value) % 256

Однако на самом деле это действительно интересный крайний случай, когда Math.abs(Integer.MIN_VALUE) == Integer.MIN_VALUE из-за переполнения.

Вместо этого используйте Math.abs для результата :

Math.abs((randomNo * value) % 256)

Я также предложу некоторую общую критику по поводу этой функции. Имена на самом деле не объясняют, что он делает. Почему generateNo? Без сомнения, есть много способов для генерации числа. Я бы предложил более конкретное имя.

Аргументы randomNo и value также проблематичны. Почему generateNo волнует, является ли первый аргумент случайным или нет?

Более точное указание того, что вы хотите, и наличие имен, описывающих эти вещи, может облегчить понимание.

Я также предлагаю, когда возникают такие проблемы, разбить шаги, чтобы вы понимали, что происходит. Что-то вроде:

private static int generateNo(int randomNo, int value){
    final int product = randomNo * value;
    final int result = product % 256;

    // Breakpoint or System.out.println here, to understand the values...
    return result;
}
0 голосов
/ 12 ноября 2018

17719*197920 = 3506944480, что больше Integer.MAX_VALUE.

Таким образом, умножение переполняет диапазон int, и в результате получается -788022816.

Следовательно, взятие модуля приводит к отрицательному результату.

...