Использование побитового ИЛИ 0 для заполнения номера

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

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

Мы говорили об этом и задавались вопросом несколько вещей.

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

Спасибо.

Ответ 1

Как это работает? Наша теория заключалась в том, что использование такого оператора бросает число до целого числа, таким образом удаляя дробную часть

Все побитовые операции, кроме сдвига без знака, >>>, работают с подписанными 32-битными целыми числами. Таким образом, использование побитовых операций преобразует float в целое число.

Есть ли у него какие-либо преимущества перед выполнением Math.floor? Может быть, это немного Быстрее? (каламбур не предназначен)

http://jsperf.com/or-vs-floor/2 кажется немного быстрее

Есть ли у него какие-то недостатки? Может быть, в некоторых случаях это не работает? Ясность очевидна, поскольку нам нужно было понять это, и, Я задаю этот вопрос.

  • Не будет проходить jsLint.
  • Только 32-разрядные целые числа со знаком
  • Нечетное Сравнительное поведение: Math.floor(NaN) === NaN, а (NaN | 0) === 0

Ответ 2

Это усечение в отличие от напольного покрытия. Ответ Говарда выглядит правильно; Но я бы добавил, что Math.floor делает именно то, что он предположительно относится к отрицательным числам. Математически это то, что слово.

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

Ответ 3

В 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

Ответ 4

Ваша первая точка верна. Число преобразуется в целое число, и поэтому любые десятичные разряды удаляются. Обратите внимание, что Math.floor округляет до следующего целого числа до минус бесконечности и, таким образом, дает другой результат при применении к отрицательным числам.

Ответ 5

  • В спецификациях говорится, что оно конвертируется в целое число:

    Пусть lnum будет ToInt32 (lval).

  • Производительность: это было проверено в jsperf раньше.

примечание: мертвая ссылка на спецификацию удалена

Ответ 6

Javascript представляет Number как 64-битные числа с плавающей запятой двойной точности.

Math.floor работает с этим в виду.

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

Итак, когда вы делаете | 0 | 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);
}