Использование побитового ИЛИ 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);
}