Целочисленное деление с остатком в JavaScript?

Ответы:

1241

Для некоторого числа yи некоторого делителя xвычислить частное ( quotient) и остаток ( remainder) как:

var quotient = Math.floor(y/x);
var remainder = y % x;
Марк Эллиот
источник
84
% работает с плавающей точкой в ​​JavaScript (это отличается от многих других языков), что, возможно, нежелательно: 3.5 % 2оценивается в 1,5. Обязательно обрабатывайте (parseInt, floor и т. Д.) Как требуется
16
Неотъемлемой частью -4,5 в математике является -5, так как -5 является "максимально возможным целым числом, которое все еще меньше, чем -4,5".
Жесткий
9
Однако, что бы вы ни решили делать с отрицательными числами, оно должно быть согласованным по частному и оставшейся части. Использование floorи %вместе не согласовано таким образом. Либо используйте truncвместо floor(тем самым разрешая отрицательные остатки), либо используйте вычитание, чтобы получить остаток ( rem = y - div * x).
Марк Рид
7
1. Если вы собираетесь вычислить остаток в remлюбом случае, вы можете получить частное divбыстрее без пола: (y - rem) / x. 2. Кстати, операция по модулю в соответствии с рекомендуемым определением Дональда Кнута (знак-совпадение-делитель, а не остаток, то есть евклидов модуль, или знак-совпадение-дивиденд JavaScript) - это то, что мы можем кодировать в JavaScript как function mod (a, n) { return a % n + (Math.sign(a) !== Math.sign(n) ? n : 0); }.
Аарон Мансхайм
1
-9 / 2 = -4,5. Затем вы берете слово -4,5, что составляет -5. Помните, что -5 меньше -4,5, и минимальная операция определяется как наибольшее целое число меньше заданного значения.
Марк Рид
371

Я не эксперт в побитовых операторах, но вот еще один способ получить целое число:

var num = ~~(a / b);

Это будет работать правильно и для отрицательных чисел, а также Math.floor()будет округлено в неправильном направлении.

Это также кажется правильным:

var num = (a / b) >> 0;
user113716
источник
84
Другой, цель которого я только что провел последние 20 минут, пытаясь выяснить, по-видимому,a/b | 0
BlueRaja - Дэнни Пфлюгофт
18
@ user113716 @BlueRaja Битовые операции имеют смысл только для целочисленных типов, и JS (конечно) это знает. ~~int, int | 0и int >> 0не изменяет начальный аргумент, но заставляет интерпретатор передавать неотъемлемую часть оператору.
Алексей Забродский
15
floorвряд ли поворачивает в неправильном направлении, учитывая его имя - просто не то направление, которое люди обычно хотят, хотя!
Марк К Коуэн
19
Это Буу Буу. a = 12447132275286670000; b = 128 Math.floor(a/b)-> 97243220900677100и ~~(a/b)-> -1231452688.
Мирек Русин
7
Будьте осторожны с приоритетом. ~~(5/2) --> 2как делает (5/2)>>0 --> 2, но ~~(5/2) + 1 --> 3пока ~~(5/2)>>0 + 1 --> 1. ~~это хороший выбор, потому что приоритет более уместен.
Тимкай
218

Я сделал несколько тестов скорости на Firefox.

-100/3             // -33.33..., 0.3663 millisec
Math.floor(-100/3) // -34,       0.5016 millisec
~~(-100/3)         // -33,       0.3619 millisec
(-100/3>>0)        // -33,       0.3632 millisec
(-100/3|0)         // -33,       0.3856 millisec
(-100-(-100%3))/3  // -33,       0.3591 millisec

/* a=-100, b=3 */
a/b                // -33.33..., 0.4863 millisec
Math.floor(a/b)    // -34,       0.6019 millisec
~~(a/b)            // -33,       0.5148 millisec
(a/b>>0)           // -33,       0.5048 millisec
(a/b|0)            // -33,       0.5078 millisec
(a-(a%b))/b        // -33,       0.6649 millisec

Вышеуказанное основано на 10 миллионах испытаний для каждого.

Вывод: используйте (a/b>>0)(или (~~(a/b))или (a/b|0)), чтобы добиться повышения эффективности примерно на 20%. Также имейте в виду, что все они несовместимы с Math.floor, когда a/b<0 && a%b!=0.

Kalel
источник
54
Обратите внимание, что оптимизация целочисленного деления для скорости имеет смысл, только если вы делаете это много . В любом другом случае я бы порекомендовал выбрать самый простой вариант (который кажется вам и вашим коллегам самым простым).
mik01aj
7
@ m01 полностью согласен - слишком много внимания уделяется таким
вещам
2
@ m01 Но что сложнее: узнать о Math.floorдругих функциях API «кто знает, сколько» или узнать об ~операторе (побитовом нет) и о том, как побитовые операции работают в JS, а затем понять эффект двойной тильды?
Стейн де Витт
11
Что ж, если ваши коллеги не программируют микросхемы на ассемблере, они, вероятно, поймут Math.floorлучше. И даже если нет, то этот является Googleable.
mik01aj
2
@MarkGreen - да, но просто желание сделать это не означает, что вы должны писать это в странной форме только потому, что это происходит быстрее всего, без веской причины - ясность кода в целом обычно должна быть вашей первой заботой. Кроме того, эти тесты могут быть совершенно бессмысленными после смены языка и в разных браузерах - вам придется профилировать, чтобы узнать, что в вашем приложении медленное, независимо от того. Это вряд ли будет ваш метод целочисленного деления, если вы уже не оптимизировали ни одного другого!
JonnyRaa
150

ES6 представляет новый Math.truncметод. Это позволяет исправить ответ @ MarkElliot, чтобы он работал и для отрицательных чисел:

var div = Math.trunc(y/x);
var rem = y % x;

Обратите внимание, что Mathметоды имеют преимущество перед побитовыми операторами в том, что они работают с числами свыше 2 31 .

Ориоль
источник
var y = 18014398509481984; var x = 5; div =? - ошибка?
4esn0k
4
@ 4esn0k Это не ошибка. Ваш номер имеет слишком много цифр, вы не можете иметь такую ​​точность в 64-битном двоичном формате IEEE 754. Например, 18014398509481984 == 18014398509481985.
Oriol
18014398509481984 == 2 ** 54, и я специально использовал это число, потому что оно представлено точно в формате двоичного 64. и ответ представляется точно тоже
4esn0k
1
Я думаю, что выбор прост: вам нужна поддержка чисел до 32 бит со знаком? Использование ~~(x/y). Нужно поддерживать большие числа до 54 битов со знаком? Используйте, Math.truncесли у вас есть, или Math.floorиным образом (исправить для отрицательных чисел). Нужно поддерживать еще большие цифры? Используйте некоторую библиотеку больших чисел.
Стейн де Витт
4
для рубиновиков здесь из гугла в поисках divmod, вы можете реализовать это так:function divmod(x, y) { var div = Math.trunc(x/y); var rem = x % y; return [div, rem]; }
Алекс Мур-Ниеми
29
var remainder = x % y;
return (x - remainder) / y;
gammax
источник
1
Эта версия, к сожалению, не проходит тест, когда x = -100, потому что возвращает -34 вместо -33.
Самуил
1
что насчет "var x = 0.3; var y = 0.01;" ? (спасибо github.com/JuliaLang/julia/issues/4156#issuecomment-23324163 )
4esn0k
На самом деле, @Samuel, с отрицательными значениями, этот метод возвращает правильные результаты, или, по крайней мере, он возвращает те же значения, что и метод, использующий Math.trunc:). Я проверил с 100,3; -100,3; 100, -3 и -100, -3. Конечно, прошло много времени с тех пор, как ваш комментарий изменился.
Марьян Венема
19

Я обычно использую:

const quotient =  (a - a % b) / b;
const remainder = a % b;

Это, вероятно, не самый элегантный, но это работает.

Волк элькан
источник
1
Хорошее решение, потому что оно позволяет избежать уродства разбора или усечения поплавка.
Дем
6
если вам нужно и частное, и остаток, сначала вычислите остаток, а затем повторно используйте это значение в выражении для частного, то есть частное = (a - остаток) / b;
gb96
2
остаток = a% b; частное = (а - остаток) / б;
Zv_oDD
16

Вы можете использовать функцию, parseIntчтобы получить усеченный результат.

parseInt(a/b)

Чтобы получить остаток, используйте оператор мод:

a%b

У parseInt есть некоторые подводные камни со строками, чтобы избежать использования параметра radix с основанием 10

parseInt("09", 10)

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

parseInt(100000000000000000000000000000000, 10) // 1e+32

Этот вызов даст 1 как результат.

Эдипо Коста Ребушас
источник
7
parseIntследует избегать, когда это возможно. Вот предупреждение Дугласа Крокфорда: «Если первый символ строки равен 0, то строка оценивается в базе 8 вместо базы 10. В базе 8, 8 и 9 не являются цифрами, поэтому parseInt (" 08 ") и parseInt ("09") в качестве результата выдаются 0. Эта ошибка вызывает проблемы в программах, которые анализируют даты и время. К счастью, parseInt может принимать параметр radix, так что parseInt ("08", 10) выдает 8. Я рекомендую вам всегда предоставить параметр radix. " archive.oreilly.com/pub/a/javascript/excerpts/...
Пауэрс
3
В подразделении я ожидаю получить число, а не строку, но это хороший момент.
Эдипо Коста Ребушас
2
@ Сила, так что добавь основание. Он не говорит, что parseIntследует избегать; Просто есть некоторые ошибки, о которых нужно знать. Вы должны знать об этих вещах и быть готовыми справиться.
Нет
2
Никогда не звоните parseIntс аргументом числа. parseIntдолжен анализировать частично-числовые строки, а не усекать числа.
Ориоль
1
То, что вещи изначально не предназначены для использования определенным образом, не означает, что вы не должны. Этот ответ работает.
fregante
6

JavaScript вычисляет прямо пол отрицательных чисел и остаток нецелых чисел, следуя математическим определениям для них.

FLOOR определяется как «наибольшее целое число, меньшее, чем параметр», таким образом:

  • положительные числа: FLOOR (X) = целая часть X;
  • отрицательные числа: FLOOR (X) = целая часть X минус 1 (потому что она должна быть МЕНЬШЕ, чем параметр, т. е. более отрицательной!)

REMAINDER определяется как «остаток» от деления (евклидова арифметика). Когда дивиденд не является целым числом, частное, как правило, также не является целым числом, то есть нет остатка, но если частное вынуждено быть целым числом (и это то, что происходит, когда кто-то пытается получить остаток или модуль число с плавающей запятой), очевидно, будет нецелое «оставшееся».

JavaScript действительно рассчитывает все, как и ожидалось, поэтому программист должен быть осторожным, чтобы задавать правильные вопросы (и люди должны быть осторожны, чтобы ответить на то, что спрашивают!) Первый вопрос Ярина был НЕ «что такое целочисленное деление X на Y», но, вместо этого, «ВЕСЬ количество раз, когда данное целое число ПРОХОДИТ В другое». Для положительных чисел ответ одинаков для обоих, но не для отрицательных, потому что целочисленное деление (делитель на делитель) будет на -1 меньше, чем число, которое (делитель) «переходит» в другое (делимое). Другими словами, FLOOR вернет правильный ответ для целочисленного деления отрицательного числа, но Ярин этого не спрашивал!

gammax правильно ответил, что код работает так, как спросил Ярин. С другой стороны, Самуил не прав, он, наверное, не делал математику, иначе он бы увидел, что это работает (также он не сказал, что было делителем его примера, но я надеюсь, что это было 3):

Остаток = X% Y = -100% 3 = -1

GoesInto = (X - остаток) / Y = (-100 - -1) / 3 = -99 / 3 = -33

Кстати, я протестировал код на Firefox 27.0.1, он работал как положено, с положительными и отрицательными числами, а также с нецелыми значениями, как для делимого, так и для делителя. Пример:

-100,34 / 3,57: GoesInto = -28, остаток = -0,3800000000000079

Да, я заметил, что здесь есть проблема точности, но у меня не было времени проверить ее (я не знаю, проблема ли это с Firefox, Windows 7 или с FPU моего процессора). Для вопроса Ярина, который включает только целые числа, код gammax работает отлично.

Cyberknight
источник
5

Math.floor(operation) возвращает округленное значение операции.

Пример 1- го вопроса:

var x = 5;
var y = 10.4;
var z = Math.floor(x + y);

console.log(z);

Приставка:

15

Пример 2 - й вопрос:

var x = 14;
var y = 5;
var z = Math.floor(x%y);

console.log(x);

Приставка:

4

Aetricity
источник
2

Вычисление количества страниц может быть сделано за один шаг: Math.ceil (x / y)

user1920925
источник
Я не вижу, как это обеспечивает остаток.
Пол Руни
1

Комментарий Алекса Мура-Ниеми как ответ:

Для Rubyists здесь от Google в поисках divmod, вы можете реализовать это так:

function divmod(x, y) {
  var div = Math.trunc(x/y);
  var rem = x % y;
  return [div, rem];
}

Результат:

// [2, 33]
Alex
источник
2
Обычно divmodиспользует floored Division ( Math.floor), который отличается от усеченного Division ( Math.trunc), когда задействованы отрицательные числа. Это относится и к пакету NPMdivmod , Rubydivmod , SWI-Prologdivmod и, возможно, ко многим другим реализациям.
Палек
Усеченное деление дает более естественно выглядящие результаты, чем расслоенное деление, но совместимость важнее, IMO. Может быть, есть и математические причины, или соображения производительности для использования разделенного пола. Обратите внимание, что обычно divmodсуществует, потому что он выполняет в два раза быстрее, чем вычисление двух операций по отдельности. Предоставление такой функции без этого выигрыша в производительности может сбить с толку.
Палек
1

Если вы просто делитесь со степенью двойки, вы можете использовать побитовые операторы:

export function divideBy2(num) {
  return [num >> 1, num & 1];
}

export function divideBy4(num) {
  return [num >> 2, num & 3];
}

export function divideBy8(num) {
  return [num >> 3, num & 7];
}

(Первое является частным, второе - оставшимся)

Константин Мёллерс
источник
Как правило, function divideByPowerOf2(num, exponent) { return [num >> exponent, num & ((1 << exponent) - 1)]; }.
Палек,
0

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

var myInt = (y > 0) ? Math.floor(y/x) : Math.floor(y/x) + 1

Если число положительное, все в порядке. Если число отрицательное, оно добавит 1 из-за того, как Math.floor обрабатывает отрицания.

Джон Галт
источник
0

Это всегда будет обрезаться до нуля. Не уверен, что уже слишком поздно, но вот оно:

function intdiv(dividend, divisor) { 
    divisor = divisor - divisor % 1;
    if (divisor == 0) throw new Error("division by zero");
    dividend = dividend - dividend % 1;
    var rem = dividend % divisor;
    return { 
        remainder: rem, 
        quotient: (dividend - rem) / divisor
    };
}
bzim
источник
0

Если вам нужно вычислить остаток для очень больших целых чисел, которые среда выполнения JS не может представить как таковая (любое целое число больше 2 ^ 32 представляется как число с плавающей точкой, и поэтому оно теряет точность), вам нужно сделать некоторый трюк.

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

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

str = '123456789123456789123456789'

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

digits = 9 - String(divisor).length

Подготовьте регулярное выражение для разбиения строки

splitter = new RegExp(`.{1,${digits}}(?=(.{${digits}})+$)`, 'g')

Например, если digits7, регулярное выражение

/.{1,7}(?=(.{7})+$)/g

Он соответствует непустой подстроке максимальной длины 7, за которой следует ( (?=...)является положительным взглядом) количество символов, кратное 7. «g» - заставить выражение проходить по всей строке, не останавливаясь при первом совпадении.

Теперь преобразуйте каждую часть в целое число и вычислите остатки reduce(добавив обратно предыдущий остаток - или 0 - умноженный на правильную степень 10):

reducer = (rem, piece) => (rem * Math.pow(10, digits) + piece) % divisor

Это будет работать из-за алгоритма остатка «вычитания»:

n mod d = (n - kd) mod d

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

Окончательный код будет выглядеть так:

function remainder(num, div) {
  const digits = 9 - String(div).length;
  const splitter = new RegExp(`.{1,${digits}}(?=(.{${digits}})+$)`, 'g');
  const mult = Math.pow(10, digits);
  const reducer = (rem, piece) => (rem * mult + piece) % div;

  return str.match(splitter).map(Number).reduce(reducer, 0);
}
переписан
источник