Лучший способ заставить модуль Java вести себя так, как должно с отрицательными числами? - PullRequest
92 голосов
/ 10 декабря 2010

В Java, когда вы делаете

a % b

Если a отрицательный, он вернет отрицательный результат, вместо того, чтобы обернуться вокруг b, как это должно быть.Какой лучший способ это исправить?Единственный способ, которым я могу думать, это

a < 0 ? b + a : a % b

Ответы [ 6 ]

125 голосов
/ 10 декабря 2010

ведет себя как следует a% b = a - a / b * b;т.е. это остаток.

Вы можете сделать (a% b + b)% b


Это выражение работает, так как результат (a % b) обязательно ниже, чем b,независимо от того, является ли a положительным или отрицательным.Добавление b учитывает отрицательные значения a, поскольку (a % b) является отрицательным значением между -b и 0, (a % b + b) обязательно ниже, чем b и положительным.Последнее по модулю существует в случае, если a было положительным для начала, так как если a положительно, (a % b + b) станет больше, чем b.Следовательно, (a % b + b) % b снова превращает его в значение, меньшее b (и не влияет на отрицательные значения a).

80 голосов
/ 14 сентября 2014

Начиная с Java 8, вы можете использовать Math.floorMod (int x, int y) и Math.floorMod (long x, long y) . Оба эти метода возвращают те же результаты, что и ответ Питера.

Math.floorMod( 2,  3) =  2
Math.floorMod(-2,  3) =  1
Math.floorMod( 2, -3) = -1
Math.floorMod(-2, -3) = -2
11 голосов
/ 25 марта 2016

Для тех, кто еще не использует (или не может использовать) Java 8, на помощь пришел Guava с IntMath.mod () , доступным с Guava 11.0.Одно предостережение: в отличие от Java 8 Math.floorMod (), делитель (второй параметр) не может быть отрицательным.

7 голосов
/ 10 июня 2016

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

= MOD (-4,180) = 176 = MOD (176, 180) = 176

потому что 180 * (-1) + 176 = -4 - это то же самое, что и 180 * 0 + 176 = 176

Используя здесь пример часов, http://mathworld.wolfram.com/Congruence.html вы бы не сказали продолжительности mod_le_time mod_clengthэто -45 минут, вы бы сказали 15 минут, хотя оба ответа удовлетворяют базовому уравнению.

1 голос
/ 01 октября 2018

Java 8 имеет Math.floorMod, но это очень медленно (его реализация имеет несколько делений, умножений и условных выражений).Вполне возможно, что JVM имеет встроенную оптимизированную заглушку, что значительно ускорит ее.

Самый быстрый способ сделать это без floorMod подобен некоторым другим ответам здесь, но без условных переходови только один медленный % op.

Предполагается, что n положительно, а x может быть любым:

int remainder = (x % n); // may be negative if x is negative
//if remainder is negative, adds n, otherwise adds 0
return ((remainder >> 31) & n) + remainder;

Результаты, когда n = 3:

x | result
----------
-4| 2
-3| 0
-2| 1
-1| 2
 0| 0
 1| 1
 2| 2
 3| 0
 4| 1

Если вам нужно только равномерное распределение между 0 и n-1, а не точный оператор мода, и ваши x не кластеризуются вблизи 0, следующее будет еще быстрее, так как есть больше инструкцийпараллелизм уровня и медленное вычисление % будут происходить параллельно с другими частями, поскольку они не зависят от его результата.

return ((x >> 31) & (n - 1)) + (x % n)

Результаты для вышеупомянутого с n = 3:

x | result
----------
-5| 0
-4| 1
-3| 2
-2| 0
-1| 1
 0| 0
 1| 1
 2| 2
 3| 0
 4| 1
 5| 2

Если входной сигнал является случайным во всем диапазоне int, распределениеоба решения будут одинаковыми.Если входные кластеры близки к нулю, в последнем решении будет слишком мало результатов при n - 1.

0 голосов
/ 31 января 2017

Вот альтернатива:

a < 0 ? b-1 - (-a-1) % b : a % b

Это может или не может быть быстрее, чем другая формула [(a% b + b)% b], если подумать.Он содержит ветку, которая обычно плохо работает с современными процессорами, но использует на одну операцию меньше по модулю.

На самом деле она может определенно быть медленнее.

(Правка: исправлена ​​формула).

...