JavaScript% (modulo) дает отрицательный результат для отрицательных чисел

Согласно Google Calculator (-13) % 64 составляет 51 .

Согласно Javascript (см. Этот JSBin ) это -13 .

Как это исправить?

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

Взято из этой статьи: JavaScript Modulo Bug

Использование Number.prototype является SLOW, потому что каждый раз, когда вы используете метод прототипа, ваш номер обернут в 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% быстрее, чем использование прототипа. Если для вас важна производительность, конечно.

Оператор % в JavaScript является оператором остатка, а не модульным оператором (основное отличие заключается в том, как обрабатываются отрицательные числа):

-1 % 8 // -1, not 7

В любом случае, это учебник с функцией «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 

Принятый ответ заставляет меня немного нервничать, потому что он повторно использует оператора%. Что делать, если 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 

Хотя это не ведет себя так, как вы ожидали, это не значит, что JavaScript не «ведет себя». Это выбор JavaScript, сделанный для его модульного вычисления. Потому что, по определению, любой ответ имеет смысл.

См. Это из Википедии. Вы можете видеть справа, как разные языки выбрали знак результата.

Если x – целое число, а n – степень 2, вы можете использовать x & (n - 1) вместо x % n .

 > -13 & (64 - 1) 51 

Похоже, что если вы пытаетесь объединить gradleусы (так что если у вас есть -50 gradleусов – 200 gradleусов), вы бы хотели использовать что-то вроде:

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

Работа с модулем JavaScript

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

Компьютеры – это точные научные инструменты, но они работают, манипулируя объектами в дискретных пространствах (у вас ограниченное количество пикселей на экране, есть ограниченное количество бит, которые стоят за каждым числом и т. Д.),

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

Оператор Modulo

Иногда ситуации осложняются ложно рекламируемыми или понятными функциями фреймворка или операторами. В этой статье основное внимание уделяется оператору modulo.

Спросите любого программиста на C # или JavaScript, что является модульным оператором на их языке, и есть большая вероятность, что они ответят:% (например, знак процента). Многие документы относятся к знаку% как к модулю оператора.

Вау! Это тонкая, но очень опасная ошибка. В операторе C # и JavaScript% фактически используется для вычисления остатка (со знаком), оставшегося, когда один операнд делится на второй операнд. Поэтому операнд следует корректно называть подписанным оператором остатка.

На первый взгляд подписанный оператор останова функционирует аналогично модулю оператора. Давайте проведем некоторые тесты, сравнив результаты, возвращаемые JavaScript, с теми, которые были возвращены Google.

В Chrome откройте консоль (нажмите F12 и выберите вкладку Консоль). Введите там, один за другим, вычисления из левого столбца. Затем введите те же выражения в панели поиска Google. Обратите внимание на результаты. Они должны быть одинаковыми.

  JavaScript Google 5 % 3 2 2 26 % 26 0 0 15 % 12 3 3 

Давайте попробуем использовать отрицательное значение в качестве первого операнда:

введите описание изображения здесь

Сюрприз!

-5% 3 = 1 (согласно Google) -5% 3 = -2 (согласно JavaScript)

Ну … это не должно быть на самом деле сюрпризом, если мы посмотрим на определение оператора% в JavaScript (… или даже на C # или на многих других языках). Google вычисляет истинное значение по модулю, в то время как эти языки компьютера вычисляют подписанное напоминание.

Однако не все языки программирования / frameworks имеют одну и ту же реализацию для%. Например, в Python оператор% вычисляет истинное значение по модулю так же, как и Google.

введите описание изображения здесь

Эта разница в поведении между языками может привести к небольшим ошибкам в вашем расчете, особенно если вы пытаетесь перенести алгоритм с одного языка на другой!

Проблема, которую понимает проблема, частично решена

Предположим, нам нужно реализовать (научный) расчет в JavaScript с использованием арифметики по модулю.

Поскольку теперь мы понимаем, что JavaScript не имеет истинного оператора modulo, мы можем легко реализовать нашу модульную операцию как функцию.

Существует несколько способов реализации modulo в JavaScript. Я покажу вам 3 способа сделать это.

 // Implement modulo by replacing the negative operand // with an equivalent positive operand that has the same wrap-around effect function mod(n, p) { if ( n < 0 ) n = p - Math.abs(n) % p; return n % p; } // Implement modulo by relying on the fact that the negative remainder // is always p numbers away from a positive reminder // Ex: -5 % 3 | -5 = -2 * 3 + 1 and -5 = -1 * 3 + (-2) | -2 + 3 = 1 function mod(n, p) { var r = n % p; return r < 0 ? r + p : r; } // Implement modulo by solving n = v * p + r equation function mod(n, p) { return n - p * Math.floor( n / p ); } 

С более точными инструментами в нашем распоряжении мы теперь готовы решать этот (научный) расчет и рассчитывать на получение правильных результатов каждый раз.

Примечание. Существует множество расчетов, которые используют модульную арифметику ... Если вы хотите увидеть, как использовать эти новые модульные функции при реализации кода Caesar Cipher / ROT13, вы можете проверить эту статью .

Я занимаюсь неграмотными и отрицательными 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); } 

Это не ошибка, есть 3 функции для вычисления по модулю, вы можете использовать ту, которая соответствует вашим потребностям (я бы рекомендовал использовать функцию Euclidean)

Усечение функции десятичной части

 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 

Существует пакет 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

Давайте будем гением компьютера.