Ціле ділення з залишком у JavaScript?


Відповіді:


1239

Для деякої кількості 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, а операція підлоги визначається як найбільше ціле число, менше заданого значення.
Марк Рід

370

Я не фахівець з розрядних операторів, але ось ще один спосіб отримати цілу кількість:

var num = ~~(a / b);

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

Це також здається правильним:

var num = (a / b) >> 0;

84
Ще один, з метою якого я щойно провів останні 20 хвилин, намагаючись розібратися, - очевидноa/b | 0
BlueRaja - Danny Pflughoeft

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. ~~є хорошим вибором, оскільки пріоритет є більш доречним.
темкай

217

Я зробив кілька тестів на швидкість на 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.


54
Зауважте, що оптимізувати ціле ділення на швидкість має сенс лише в тому випадку, якщо ви це робите багато . У будь-якому іншому випадку я рекомендую вибрати найпростіший (той, хто здається найпростішим для вас та ваших колег).
mik01aj

7
@ m01 повністю погоджуюся - є занадто багато уваги на такі речі в Інтернеті
JonnyRaa

2
@ m01 Але що складніше: дізнатись про, Math.floorа також, хто знає, як багато інших функцій API, або дізнатися про ~(бітовий-не) оператор і як бітові операції працюють у JS, а потім зрозуміти ефект подвійного нахилу?
Штійн де Вітт

11
Ну, якщо ваші колеги не програмують чіпи в асемблері, вони, швидше за все, зрозуміють Math.floorкраще. І навіть якщо ні, цей є гугл.
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.
Оріол

18014398509481984 == 2 ** 54, і я спеціально використовував це число, оскільки воно представлено саме у форматі бінарного64. і відповідь представлена ​​точно теж
4esn0k

1
Я думаю, що вибір простий: вам потрібна підтримка чисел до 32 біт, підписаних? Використовуйте ~~(x/y). Вам потрібно підтримувати більшу кількість до 54 біт, підписаних? Використовуйте, Math.truncякщо у вас є, або Math.floorіншим способом (правильно для від'ємних чисел). Вам потрібно підтримувати ще більшу кількість? Використовуйте бібліотеку великих чисел
Штійн де Вітт

4
для рубістів тут з google в пошуках divmod, ви можете реалізувати це як таке:function divmod(x, y) { var div = Math.trunc(x/y); var rem = x % y; return [div, rem]; }
Alex Moore-Niemi

29
var remainder = x % y;
return (x - remainder) / y;

1
На жаль, ця версія, на жаль, не відповідає тесту, коли x = -100, оскільки вона повертає -34 замість -33.
Самуїл

1
як щодо "var x = 0,3; var y = 0,01;" ? (спасибі github.com/JuliaLang/julia/isissue/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
Приємне рішення, оскільки воно дозволяє уникнути потворності розбору або обрізання поплавця.
Dem Pilafian

6
якщо вам потрібні і коефіцієнт, і залишок, спочатку обчисліть залишок, а потім повторно використовуйте це значення у виразі для коефіцієнта, тобто коефіцієнт = (a - залишок) / b;
gb96

2
залишок = a% b; коефіцієнт = (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
У підрозділі я очікую отримання числа, а не рядка, але це хороший момент.
Édipo Costa Rebouças

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 мого процесора). Для запитання Яріна, що стосується лише цілих чисел, код гаммакса працює чудово.


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


2

Обчислення кількості сторінок може бути виконано за один крок: Math.ceil (x / y)


Я не бачу, як це забезпечує решту.
Пол Руні

1

Коментар Алекса Мура-Ніємі як відповідь:

Для рубістів тут від Google divmod, ви можете їх реалізувати як таке:

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

Результат:

// [2, 33]

2
Зазвичай divmodвикористовується флоральний поділ ( Math.floor), який відрізняється від усіченого поділу ( Math.trunc), коли задіяні негативні числа. Це стосується пакету NPMdivmod , Rubydivmod , SWI-Prologdivmod і, мабуть, багатьох інших реалізацій.
Палець

Урізаний поділ дає більш природно виглядаючі результати, ніж підпорядкований поділ, але суперечить цьому сумісність, ІМО. Можливо, є і математичні причини, або продуктивність, і для використання підпорядкованого поділу. Зауважте, що, як правило, 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
    };
}

0

Якщо вам потрібно обчислити залишок для дуже великих цілих чисел, які час виконання JS не може представляти як таке (будь-яке ціле число, що перевищує 2 ^ 32, представлено у вигляді поплавця і тому воно втрачає точність), вам потрібно зробити якийсь трюк.

Це особливо важливо для перевірки багатьох випадків чекових цифр, які є у багатьох випадках нашого повсякденного життя (номери банківських рахунків, кредитні картки, ...)

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

str = '123456789123456789123456789'

Тепер вам потрібно розділити рядок на більш дрібні частини, досить малі, щоб з'єднання будь-якого залишку та шматка рядка могло вміститися в 9 цифр.

digits = 9 - String(divisor).length

Підготуйте регулярний вираз для розділення рядка

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

Наприклад, якщо digitsце 7, регулярне вираження є

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

Він відповідає непустому підрядку максимальної довжини 7, за яким слідує ( (?=...)є позитивним підказкою) число символів, кратне 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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.