Як Java виконує обчислення модуля з від’ємними числами?


93

Я роблю модуль неправильно? Тому що в Java -13 % 64передбачається оцінювати до, -13але я отримую 51.


102
@Dan Навіть без статичної головної порожнечі ... я бачу код.
Джоріс Мейс,

22
Я отримую -13 & 64 == -13
Гродрігес

7
Як це ви отримуєте 51 тариф за -13.
Raedwald,

3
Ви зовсім не робите модуль. У Java немає оператора за модулем. %є оператором залишку.
Маркіз Лорнський

3
Заплутане запитання: Java 8 дає -13, як кажуть деякі інші люди. З якою версією Java ви нібито це придбали?
Ян Янковський,

Відповіді:


104

Використовуються обидва визначення модуля від’ємних чисел - деякі мови використовують одне визначення, а інші - інше.

Якщо ви хочете отримати від’ємне число для негативних входів, тоді ви можете використовувати це:

int r = x % n;
if (r > 0 && x < 0)
{
    r -= n;
}

Подібним чином, якщо ви використовували мову, яка повертає від'ємне число на від'ємне введення, і ви б віддали перевагу позитивному:

int r = x % n;
if (r < 0)
{
    r += n;
}

3
Це не працює добре, якщо n від’ємне. Якщо ви використовуєте той самий приклад із Java 7 Lang Spec (Розділ 15.17.3): (-5)% (-3) = -2. Додавання -3 не буде працювати. Вам слід додати абсолютне значення n, якщо ви хочете бути впевненим, що значення є позитивним.
partlov

6
У Java негативний модуль нічого не змінює, якщо ви все одно використовуєте Abs (), просто напишіть r = x% abs (n). Мені не подобається if твердження, я волів би написати r = ((x% n) + n)% n. Щодо потужності 2 за модулем (2,4,8,16 тощо) та позитивної відповіді, розглянемо двійкову маску r = x & 63.
Фабієн,

3
У контексті Java (відповідно до тегу запитання) ця відповідь по суті "неправильна". Враховуючи вираз x % y, А) якщо xвід’ємне, то залишок від’ємне, тобто x % y == -(-x % y). Б) знак yне має ефекту, тобтоx % y == x % -y
богемський

72

Оскільки "математично" обидва правильні:

-13 % 64 = -13 (on modulus 64)  
-13 % 64 = 51 (on modulus 64)

Один із варіантів повинен був бути обраний розробниками мови Java, і вони вибрали:

знак результату дорівнює знаку дивіденду.

Каже це в специфікаціях Java:

https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.17.3


8
Питання в тому, "чому Java дає мені, -13 % 64 = 51коли я очікував -13?".
Паскаль Куок,

5
@pascal: java дає вам правильне визначення в математиці та спосіб її реалізації, а не те, що ви очікуєте від неї.

9
Математично розумна поведінка доступна в Java 8: Math.floorMod
Джессі Глік

1
Щось у їх 15.17.3. Приклади оператора залишку% неясні. int result = (-5) % 3;дає -2. int result = (-3) % 5;дає -3. Загалом, int result = (-a) % b;дає правильну відповідь, коли | -a | > b. Для того, щоб отримати належний результат, коли | -a | <b ми повинні обернути дільник. int result = ((-a) % b) + b;за негативне а або int result = (((-a) % b) + b) % b;за позитивне чи негативне а
Оз Едрі

@Caner Я цього не зрозумів, як обидва можуть бути однаковими?
Kishore Kumar Korada

20

Ви впевнені, що працюєте на Java? тому що Java дає -13% 64 = -13, як очікувалося. Знак дивіденду!


15

Ваш результат неправильний для Java. Будь ласка, вкажіть трохи контексту, як ви до цього дійшли (ваша програма, реалізація та версія Java).

З специфікації мови Java

15.17.3 Оператор залишку%
[...]
Операція залишку для операндів, які є цілими числами після двійкового числового просування (§5.6.2), дає таке значення результату, що (a / b) * b + (a% b) дорівнює a.
15.17.2 Оператор підрозділу /
[...]
Ціле ділення округлюється до 0.

Оскільки / округляється до нуля (в результаті чого дорівнює нулю), результат% у цьому випадку повинен бути негативним.


Щось у їх 15.17.3. Приклади оператора залишку% не зрозумілі. int result = (-5) % 3;дає -2 int result = (-3) % 5;дає -3 Загалом, int result = (-a) % b;дає правильну відповідь, коли | -a | > b Для того, щоб отримати належний результат, коли | -a | <b ми повинні обернути дільник. int result = ((-a) % b) + b;для негативного a або int result = (((-a) % b) + b) % b;для позитивного або негативного a.
Оз Едрі,

1
Ваш коментар досить незрозумілий. Розділ визначає правильний результат, і приклади узгоджуються з цим визначенням. Для вашого прикладу (-3) % 5правильним результатом згідно з визначенням є -3, і правильна реалізація Java повинна давати цей результат.
starblue

Здається, я не правильно пояснив себе. Що я мав на увазі під "правильною відповіддю", коли | -a | <b, це те, що для отримання позитивного результату ми повинні "обернути" даний результат із% b, додавши до нього b. У моєму прикладі (-3)%5справді дає -3, і якщо ми хочемо позитивний залишок, ми повинні додати до нього 5, і тоді результат буде2
Оз Едрі

6

ви можете використовувати

(x % n) - (x < 0 ? n : 0);

3
@ruslik Ви також можете зробити: ((x % k) + k) % k. (Хоча ваш, мабуть, читабельніший.)
Джон Курлак,

2
@JohnKurlak Ваша версія працює так: 4% 3 = 1 АБО 4% -3 = -2 АБО -4% 3 = 2 АБО -4% -3 = -1, але версія від ruslik працює так: 4% 3 = 1 АБО 4% -3 = 1 АБО -4% 3 = -4 АБО -4% -3 = 2
Джошуа

1
@Joschua Дякуємо, що вказали на це. Мій код корисний, коли ви хочете, щоб результат модуля знаходився в діапазоні [0, sign(divisor) * divisor)замість [0, sign(dividend) * divisor).
Джон Курлак,

3

Ваша відповідь - у wikipedia: modulo operation

У ньому сказано, що на Java ознака операції за модулем однакова з ознакою дивіденду. а оскільки ми говоримо про решту операцій ділення, це нормально, то у вашому випадку вона повертає -13, оскільки -13/64 = 0. -13-0 = -13.

EDIT: Вибачте, неправильно зрозумів ваше запитання ... Ви маєте рацію, Java повинна дати -13. Чи можете ви надати більше оточуючого коду?


2

Модульна арифметика з негативними операндами визначається мовним дизайнером, який може залишити це на мовній реалізації, який може перенести визначення на архітектуру центрального процесора.

Мені не вдалося знайти визначення мови Java.
Завдяки Іштар, специфікація мови Java для оператора залишку% говорить, що знак результату такий самий, як знак чисельника.


1

Щоб подолати це, ви можете додавати 64(або будь-яку базу вашого модуля) до негативного значення, поки воно не буде додатним

int k = -13;
int modbase = 64;

while (k < 0) {
    k += modbase;
}

int result = k % modbase;

Результат все одно буде в тому ж класі еквівалентності.


1

x = x + m = x - mв модулі m.
так -13 = -13 + 64в модулі 64 і -13 = 51в модулі 64.
припустимо Z = X * d + r, якщо 0 < r < Xтоді при діленні Z/Xми називаємо rзалишок.
Z % Xповертає залишок Z/X.


1

Функція mod визначається як сума, на яку число перевищує найбільше ціле число, кратне дільнику, яке не більше цього числа. Тож у вашому випадку

-13 % 64

найбільше ціле число, кратне 64, яке не перевищує -13, становить -64. Тепер, коли відняти -13 від -64, це дорівнює 51-13 - (-64) = -13 + 64 = 51


0

У моїй версії Java JDK 1.8.0_05 -13% 64 = -13

ви можете спробувати -13- (int (-13/64)), іншими словами, зробити ділення приведене до цілого числа, щоб позбутися частини дробу, а потім відняти від чисельника. Отже, чисельник- (int (чисельник / знаменник)) повинен дати правильний залишок і знак


0

В останніх версіях Java ви отримуєте -13%64 = -13. У відповіді завжди буде знак чисельника.


В якій версії було внесено цю зміну?
NightShadeQueen

У Java 7 чітко згадується, що мод матиме знак числівника :)
vsn harish rayasam

@NightShadeQueen Він ніколи не змінювався.
Маркіз Лорнський,

0

Відповідно до розділу 15.17.3 JLS, "Операція залишку для операндів, які є цілими числами після двійкового числового просування, створює таке значення результату, що (a / b) * b + (a% b) дорівнює a. Ця ідентичність має навіть в особливому випадку, коли дивіденд є цілим від'ємним числом якомога більшої величини для свого типу, а дільник дорівнює -1 (залишок - 0) ".

Сподіваюся, це допомагає.


-1

Я не думаю, що в цьому випадку Java повертає 51. Я запускаю Java 8 на Mac і отримую:

-13 % 64 = -13

Програма:

public class Test {
    public static void main(String[] args) {
        int i = -13;
        int j = 64;
        System.out.println(i % j);
    }
}

5
@XaverKapeller, ні! Багато людей зазначали, що математично кажучи -13 та 51 вірні. На Java -13 очікувана відповідь, і це те, що я теж отримав, тому я не знаю, як подавець отримав 51, це загадка. Деталі режиму про контекст можуть допомогти правильно відповісти на це питання.
Фабієн,

@Xaver Kapeller: Як можуть бути правильними і 51, і -13? Java поверне лише одне значення ..
ceprateek

@XaverKapeller Як можна відповісти, що документує, що Java насправді може бути неправильним?
Маркіз Лорнський,

@EJP Я думаю, що 3 роки тому, коли я писав це, я був досить тупим, щоб оцінювати математичну точність більше, ніж просту реальність того, як Java має справу з цим. Дякую, що нагадали мені видалити мій дурний коментар: D
Ксавер Капеллер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.