JavaScript% (модуль) дає негативний результат для негативних чисел


252

За даними Google Calculator (-13) % 64 є 51.

Згідно з Javascript (див. Цей JSBin ), це так -13.

Як це виправити?


Це може бути просто пріоритетним питанням. Ви маєте на увазі (-13) % 64чи -(13 % 64)? Особисто я поставив би парень будь-яким способом, просто для додаткової ясності.
MatrixFrog

2
по суті, дублікат того, як java здійснює обчислення модуля з від'ємними числами? навіть якщо це питання javascript.
президент Джеймс К. Полк

85
Javascript інколи відчуває себе дуже жорстоким жартом
герцогство, яке розпочалося

6
google не може помилятися
caub

10
Принциповою проблемою в JS %є не оператор модуля. Це решта оператора. У JavaScript немає жодного модульного оператора. Тож прийнята відповідь - це шлях.
Скорочення

Відповіді:


262
Number.prototype.mod = function(n) {
    return ((this%n)+n)%n;
};

Взято з цієї статті: Помилка модуля JavaScript


23
Я не знаю, що я б назвав це "помилкою". Операція по модулю не дуже чітко визначена за від'ємними числами, і різні обчислювальні середовища обробляють її по-різному. Стаття Вікіпедії про модульну операцію досить добре висвітлює її.
Даніель Приден

22
Це може здатися німим, оскільки його часто називають «модулем», припускаючи, що він поводитиметься так само, як і його математичне визначення (див. ℤ / nℤ алгебра), чого немає.
etienne

7
Навіщо приймати модуль перед додаванням n? Чому б просто не додати n, а потім взяти модуль?
зірвався

12
@starwed, якщо ви не використовували цей% n, це не вдасться x < -n- наприклад, (-7 + 5) % 5 === -2але ((-7 % 5) + 5) % 5 == 3.
fadedbee

7
Рекомендую додати у відповідь, що для доступу до цієї функції слід використовувати формат (-13) .mod (10) замість -13% 10. Це було б більш зрозуміло.
Jp_

161

Використання Number.prototype- НИЗЬКО, тому що кожного разу, коли ви використовуєте метод прототипу, ваш номер загортається у Object. Замість цього:

Number.prototype.mod = function(n) {
  return ((this % n) + n) % n;
}

Використання:

function mod(n, m) {
  return ((n % m) + m) % m;
}

Дивіться: http://jsperf.com/negative-modulo/2

~ 97% швидше, ніж використання прототипу. Якщо продуктивність для вас важлива, звичайно.


1
Чудова порада. Я взяв ваш jsperf і порівняв з іншими рішеннями цього питання (але, здається, це все-таки найкраще): jsperf.com/negative-modulo/3
Mariano Desanze

11
Мікрооптимізація. Для цього вам доведеться робити велику кількість модних обчислень, щоб не мати ніякої різниці. Зазначте, що є найяснішим та найрентабельнішим, а потім оптимізуйте наступний аналіз продуктивності.
ChrisV

Я думаю , що у вас є ваші nз і mS навколо неправильно у вашому другому прикладі @StuR. Це повинно бути return ((n % m) + m) % m;.
віміст

Це має бути коментар до прийнятої відповіді, а не відповідь для себе.
xehpuk

5
Мотивація, зазначена у цій відповіді, - це мікрооптимізація, так, але змінити прототип проблематично. Віддавайте перевагу підходу з найменшими побічними ефектами, що саме є цим.
Кін

30

%Оператор в JavaScript оператор інше, а НЕ оператор по модулю (головна відмінність полягає в тому , як негативні числа обробляються):

-1 % 8 // -1, not 7


8
Він повинен бути названий оператор залишок , але це називається модуль оператора: developer.mozilla.org/en-US/docs/Web/JavaScript/Guide / ...
Big McLargeHuge

15
@DaveKennedy: MDN не є офіційною мовою, це веб-сайт, що редагується спільнотою, який іноді помиляється. Специфікація не називає це оператором модуля, і наскільки я можу сказати, цього ніколи не було (я повернувся до ES3). Він прямо говорить, що оператор дає решту мається на увазі поділ і просто називає його "оператором%".
TJ Crowder

2
Якщо він викликається remainder, він повинен бути більшим за 0 за визначенням. Ви не можете пригадати теорему про поділ середньої школи ?! Тож, можливо, ви можете подивитися тут: en.wikipedia.org/wiki/Euclidean_division
Ахмад

19

Функція "mod" для повернення позитивного результату.

var mod = function (n, m) {
    var remain = n % m;
    return Math.floor(remain >= 0 ? remain : remain + m);
};
mod(5,22)   // 5
mod(25,22)  // 3
mod(-1,22)  // 21
mod(-2,22)  // 20
mod(0,22)   // 0
mod(-1,22)  // 21
mod(-21,22) // 1

І звичайно

mod(-13,64) // 51

1
MDN не є офіційним мовним посиланням, це веб-сайт, що редагується громадою, який іноді помиляється. Специфікація не називає це оператором модуля, і наскільки я можу сказати, цього ніколи не було (я повернувся до ES3). Він прямо говорить, що оператор дає решту мається на увазі поділ і просто називає його "оператором%".
TJ Crowder

1
На жаль, посилання, яке ви вказали насправді, посилається #sec-applying-the-mod-operatorпрямо в URL-адресі :) У будь-якому випадку, дякую за зауваження, я вийняв пух з своєї відповіді, все одно це не дуже важливо.
Shanimal

3
@ Shanimal: LOL! Це робить. Помилка редактора HTML. Текст специфікації не відповідає.
TJ Crowder

10

Прийнята відповідь мене трохи нервує, оскільки вона повторно використовує оператор%. Що робити, якщо Javascript змінить поведінку в майбутньому?

Ось вирішення, яке не використовується повторно%:

function mod(a, n) {
    return a - (n * Math.floor(a/n));
}

mod(1,64); // 1
mod(63,64); // 63
mod(64,64); // 0
mod(65,64); // 1
mod(0,64); // 0
mod(-1,64); // 63
mod(-13,64); // 51
mod(-63,64); // 1
mod(-64,64); // 0
mod(-65,64); // 63

8
Якщо javascript змінив оператор модуля, щоб він відповідав математичному визначенню, прийнята відповідь все одно спрацює.
зірвався

20
"Що робити, якщо Javascript змінить поведінку в майбутньому?" - Навіщо це? Змінити поведінку такого фундаментального оператора малоймовірно.
nnnnnn

1
+1 для того, щоб поділитися цим занепокоєнням та альтернативою викладеній відповіді # відповідь-4467559 & з 4 причин: (1) Чому він заявляє, і так «Зміна поведінки такого фундаментального заходу не є ймовірною», але все-таки доцільно враховувати. навіть знайти це не потрібно. (2) визначення робочого оператора з точки зору зламаного, але вражаюче, викликає занепокоєння принаймні на 1-му погляді, на це має бути показано не (3), тому що я hvnt добре перевірив цю альтернативу, я вважаю, що легше слідувати далі швидкий погляд. (4) крихітний: він використовує 1 div + 1 mul замість 2 (mod) divs. Я чув, що раніше багато апаратних без гарного FPU, множення було швидше.
Архітектор долі

2
@DestinyArchitect це не розсудливо, це безглуздо. Якби вони змінили поведінку оператора, що залишився, він би порушив хороший спектр програм, що використовують його. Це ніколи не відбудеться.
Егід

9
Що робити , якщо поведінка -, *, /, ;, ., (, ), ,, Math.floor, functionабо returnзміни? Тоді ваш код жахливо порушений.
xehpuk

5

Хоча це не так, як ви очікували, це не означає, що JavaScript не "поводиться". Це вибір JavaScript для його модульного розрахунку. Тому що за визначенням будь-яка відповідь має сенс.

Дивіться це з Вікіпедії. Праворуч ви бачите, як різні мови обрали знак результату.


3

Якщо xце ціле число і nдорівнює 2, ви можете використовувати x & (n - 1)замість x % n.

> -13 & (64 - 1)
51 

2

Тож здається, що якщо ви намагаєтеся змінити градуси (так що якщо у вас -50 градусів - 200 градусів), ви хочете використовувати щось на зразок:

function modrad(m) {
    return ((((180+m) % 360) + 360) % 360)-180;
}

1

Я маю справу і з негативним a, і з негативним n

 //best perf, hard to read
   function modul3(a,n){
        r = a/n | 0 ;
        if(a < 0){ 
            r += n < 0 ? 1 : -1
        }
        return a - n * r 
    }
    // shorter code
    function modul(a,n){
        return  a%n + (a < 0 && Math.abs(n)); 
    }

    //beetween perf and small code
    function modul(a,n){
        return a - n * Math[n > 0 ? 'floor' : 'ceil'](a/n); 
    }

1

Це не помилка, є 3 функції для обчислення модуля, ви можете використовувати ту, яка відповідає вашим потребам (я б рекомендував використовувати функцію Евкліда)

Обрізання функції десяткової частини

console.log(  41 %  7 ); //  6
console.log( -41 %  7 ); // -6
console.log( -41 % -7 ); // -6
console.log(  41 % -7 ); //  6

Функція цілої частини

Number.prototype.mod = function(n) {
    return ((this%n)+n)%n;
};

console.log( parseInt( 41).mod( 7) ); //  6
console.log( parseInt(-41).mod( 7) ); //  1
console.log( parseInt(-41).mod(-7) ); // -6
console.log( parseInt( 41).mod(-7) ); // -1

Евклідова функція

Number.prototype.mod = function(n) {
    var m = ((this%n)+n)%n;
    return m < 0 ? m + Math.abs(n) : m;
};

console.log( parseInt( 41).mod( 7) ); // 6
console.log( parseInt(-41).mod( 7) ); // 1
console.log( parseInt(-41).mod(-7) ); // 1
console.log( parseInt( 41).mod(-7) ); // 6

1
У евклідовій функції перевірка m <0 марна, оскільки ((цей% n) + n)% n завжди позитивний
бормат

@bormat Так, але в Javascript %можна повернути негативні результати (це і є метою цих функцій, щоб виправити це)
zessx

ви написали це [код] Number.prototype.mod = функція (n) {var m = ((це% n) + n)% n; повернути m <0? m + Math.abs (n): m; }; [/ code] дайте мені одне значення n, де m є негативним. вони не мають значення n, де m є негативним, оскільки ви додаєте n після першого%.
бормат

Без цієї перевірки parseInt(-41).mod(-7)повернеться -6замість 1(а саме це і є функція частини Integer, яку я написав)
zessx

га нормально, ти маєш рацію, всі мої вибачення, я забув негативний модуль, я думав лише про "цей" негатив. Чи можу я запропонувати перемістити Math.abs Number.prototype.mod = функцію (n) {return ((цей% n) + Math.abs (n))% n; }; (-41) .mod (-7) == 1 // немає необхідності
розборуInt

0

Є пакет NPM, який зробить роботу за вас. Ви можете встановити його за допомогою наступної команди.

npm install just-modulo --save

Використання скопійовано з програми README

import modulo from 'just-modulo';

modulo(7, 5); // 2
modulo(17, 23); // 17
modulo(16.2, 3.8); // 17
modulo(5.8, 3.4); //2.4
modulo(4, 0); // 4
modulo(-7, 5); // 3
modulo(-2, 15); // 13
modulo(-5.8, 3.4); // 1
modulo(12, -1); // NaN
modulo(-3, -8); // NaN
modulo(12, 'apple'); // NaN
modulo('bee', 9); // NaN
modulo(null, undefined); // NaN

Репозиторій GitHub можна знайти за наступним посиланням:

https://github.com/angus-c/just/tree/master/packages/number-modulo

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.