Використовуючи побітне значення АБО 0, щоб передати число


193

Моя колега натрапила на метод перекрити плаваючі числа за допомогою побітових або:

var a = 13.6 | 0; //a == 13

Ми говорили про це і цікавились декілька речей.

  • Як це працює? Наша теорія полягала в тому, що за допомогою такого оператора кідає число до цілого числа, тим самим видаляючи дробову частину
  • Чи є якісь переваги перед тим, як робити Math.floor? Може, це трохи швидше? (каламбур не призначений)
  • Чи є у нього недоліки? Можливо, це не працює в деяких випадках? Чіткість очевидна, оскільки нам довелося це розібратися, і я це пишу.

Дякую.


6
Недолік: він працює лише до 2 ^ 31−1, що становить близько 2 мільярдів (10 ^ 9). Значення максимального числа становить близько 10 ^ 308 Btw.
Šime Vidas

12
Приклад: 3000000000.1 | 0оцінює до -1294967296. Тому цей метод не можна застосовувати для розрахунків грошей (особливо у випадках, коли ви множите на 100, щоб уникнути десяткових чисел).
Šime Vidas

13
@ ŠimeVidas Floats також не слід використовувати в грошових розрахунках
Джордж Рейт

20
Це не настил, він обрізний (округлення до 0).
Bartłomiej Zalewski

3
@ паслядовність спробуйте ввести 0.1 + 0.2 == 0.3консоль JavaScript. Якщо ваша мова підтримує це, ви повинні використовувати десятковий тип. Якщо ні, натомість зберігайте центи.
Алекс Турпін

Відповіді:


161

Як це працює? Наша теорія полягала в тому, що за допомогою такого оператора кідає число до цілого числа, тим самим видаляючи дробову частину

Усі побітові операції, за винятком непідписаного правого зсуву,, >>>працюють на підписаних 32-бітових цілих числах. Тож використання побітових операцій перетворить поплавок на ціле число.

Чи є якісь переваги перед тим, як робити Math.floor? Може, це трохи швидше? (каламбур не призначений)

http://jsperf.com/or-vs-floor/2 здається трохи швидшим

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

  • Не пройде jsLint
  • Тільки 32-бітні підписані цілі числа
  • Непарне Порівняльна поведінка:, Math.floor(NaN) === NaNпоки(NaN | 0) === 0

9
@harold справді, тому що насправді він не кругле, а просто скорочується.
Алекс Турпін

5
Ще одним можливим недоліком є ​​те Math.floor(NaN) === NaN, що поки (NaN | 0) === 0. Ця різниця може бути важливою у деяких програмах.
Тед Хопп

4
Ваш jsperf отримує інформацію про продуктивність порожніх циклів на хромі через рух інваріантного коду циклу. Трохи кращий тест на парф буде: jsperf.com/floor-performance/2
Сем Гілз

4
Це стандартна частина asm.js(де я вперше дізнався про це). Це швидше, якщо з будь-якої іншої причини, оскільки це не викликає функцію на Mathоб'єкті, функцію, яку можна було в будь-який час замінити як на Math.floor = function(...).
gman

3
(value | 0) === valueможе бути використаний для перевірки того, що значення насправді є цілим числом і лише цілим числом (як у згаданому вихідному коді Elm @ dwayne-crooks). І foo = foo | 0може бути використане для примусового будь-якого значення до цілого числа (де 32-бітові числа усічені, а всі нечисленні стають 0).
Девід Майкл Грегг

36

Це укорочення на відміну від підлоги. Відповідь Говарда є якось правильною; Але я додам, що Math.floorробить саме те, що належить щодо негативних чисел. Математично це те, що є підлогою.

У випадку, який ви описали вище, програміст був більше зацікавлений у врізанні або відсіканні десятків повністю. Хоча синтаксис, який вони використовували, затьмарює той факт, що вони перетворюють float в int.


7
Це правильна відповідь, прийнята - ні. Додайте до нього, що Math.floor(8589934591.1)дає очікуваний результат, 8589934591.1 | 0 НЕ .
Салман А

21

У ECMAScript 6 еквівалент |0є Math.trunc , начебто я повинен сказати:

Повертає інтегральну частину числа, видаляючи будь-які дробові цифри. Він просто обрізає крапку та цифри за нею, незалежно від того, чи є аргументом позитивне чи негативне число.

Math.trunc(13.37)   // 13
Math.trunc(42.84)   // 42
Math.trunc(0.123)   //  0
Math.trunc(-0.123)  // -0
Math.trunc("-1.123")// -1
Math.trunc(NaN)     // NaN
Math.trunc("foo")   // NaN
Math.trunc()        // NaN

6
За винятком того, що Math.trunc()робота з числом вище або дорівнює 2 ^ 31, а | 0не
Нолюрн

10

Ваш перший пункт правильний. Число приводиться до цілого числа, і таким чином будь-які десяткові цифри видаляються. Зверніть увагу, що Math.floorокруглює наступне ціле число до мінус нескінченності і, таким чином, дає інший результат, коли застосовується до від’ємних чисел.


5

Javascript представляє Numberяк 64-бітні плаваючі числа подвійної точності .

Math.floor працює з цим на увазі.

Бітові операції працюють в 32 - бітному підписав цілі числа. Цілі числа з 32-бітним підписом використовують перший біт як негативний означувач, а інші 31 біт - це число. Через це мінімальне та максимальне число дозволених 32-бітних підписаних номерів складають -2,147,483,648 та 2147483647 (0x7FFFFFFFF) відповідно.

Отже, коли ти робиш | 0, ти по суті робиш це & 0xFFFFFFFF. Це означає, що будь-яке число, представлене як 0x80000000 (2147483648) або більше, повернеться як негативне число.

Наприклад:

 // Safe
 (2147483647.5918 & 0xFFFFFFFF) ===  2147483647
 (2147483647      & 0xFFFFFFFF) ===  2147483647
 (200.59082098    & 0xFFFFFFFF) ===  200
 (0X7FFFFFFF      & 0xFFFFFFFF) ===  0X7FFFFFFF

 // Unsafe
 (2147483648      & 0xFFFFFFFF) === -2147483648
 (-2147483649     & 0xFFFFFFFF) ===  2147483647
 (0x80000000      & 0xFFFFFFFF) === -2147483648
 (3000000000.5    & 0xFFFFFFFF) === -1294967296

Також. Побітні операції не «перекриваються». Вони усікаються , що це те саме, що й казати, вони кружляють найближче 0. Після того, як ви йдете навколо негативних чисел Math.floorраундів вниз в той час як побітовое починають округляти вгору .

Як я вже говорив раніше, Math.floorце безпечніше, оскільки він працює з 64-бітовими плаваючими номерами. Побітовое швидше , так, але обмежується 32 - бітовим підписали сферу.

Узагальнити:

  • Побіт працює так само, якщо ви працюєте з 0 to 2147483647.
  • Побітове значення - 1 число, якщо ви працюєте з -2147483647 to 0.
  • Побітове значення зовсім інше для чисел, менших -2147483648і більших за 2147483647.

Якщо ви дійсно хочете налаштувати продуктивність, використовуйте обидва:

function floor(n) {
    if (n >= 0 && n < 0x80000000) {
      return n & 0xFFFFFFFF;
    }
    if (n > -0x80000000 && n < 0) {
      return (n - 1) & 0xFFFFFFFF;
    }
    return Math.floor(n);
}

Просто додати Math.truncтвори, як бітові операції. Тож ви можете це зробити:

function trunc(n) {
    if (n > -0x80000000 && n < 0x80000000) {
      return n & 0xFFFFFFFF;
    }
    return Math.trunc(n);
}

5
  • Технічні характеристики кажуть, що воно перетворене на ціле число:

    Нехай lnum буде ToInt32 (lval).

  • Продуктивність: це було перевірено на jsperf раніше.

Примітка: мертве посилання на специфікацію видалено

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