Каков наилучший способ преобразования числа с плавающей точкой в целое число в JavaScript?
Существует несколько различных способов преобразования чисел с плавающей точкой в целые числа в JavaScript. Мой вопрос в том, какой метод дает лучшую производительность, является наиболее совместимым или считается лучшей практикой?
Вот несколько методов, о которых я знаю:
var a = 2.5;
window.parseInt(a); // 2
Math.floor(a); // 2
a | 0; // 2
Я уверен, что там есть другие. Предложения?
Ответы
Ответ 1
Согласно этот веб-сайт:
parseInt иногда используется как средство превращения числа с плавающей запятой в целое число. Он очень плохо подходит для этой задачи, потому что, если его аргумент имеет числовой тип, он сначала преобразуется в строку, а затем анализируется как число...
Для округления чисел до целых чисел предпочтительны один из Math.round, Math.ceil и Math.floor...
Ответ 2
По-видимому, двойной побитовый - это не самый быстрый способ заполнить номер:
var x = 2.5;
console.log(~~x); // 2
Используется для статьи здесь, получив теперь 404: http://james.padolsey.com/javascript/double-bitwise-not/
У Google есть кеширование: http://74.125.155.132/search?q=cache:wpZnhsbJGt0J:james.padolsey.com/javascript/double-bitwise-not/+double+bitwise+not&cd=1&hl=en&ct=clnk&gl=us
Но машина Wayback экономит день! http://web.archive.org/web/20100422040551/http://james.padolsey.com/javascript/double-bitwise-not/
Ответ 3
От "Javascript: хорошие детали" от Дугласа Крокфорда:
Number.prototype.integer = function () {
return Math[this < 0 ? 'ceil' : 'floor'](this);
}
Выполняя это, вы добавляете метод к каждому объекту Number.
Затем вы можете использовать его следующим образом:
var x = 1.2, y = -1.2;
x.integer(); // 1
y.integer(); // -1
(-10 / 3).integer(); // -3
Ответ 4
Ответ уже дан, но чтобы быть ясным.
Используйте для этого библиотеку Math. круглые, потолочные или напольные функции.
parseInt предназначен для преобразования строки в int, которая не нужна здесь
toFixed предназначен для преобразования float в строку также не в том, что здесь необходимо
Поскольку функции Math не будут делать никаких преобразований в строку или из нее, она будет быстрее любого другого выбора, который в любом случае ошибочен.
Ответ 5
Вы можете использовать Number (a).toFixed(0);
Или даже просто a.toFixed(0);
Edit:
Это округление до 0 мест, немного отличное от усечения, и, как было предложено другим, toFixed возвращает строку, а не целое число. Полезно для показа.
var num = 2.7; // typeof num is "Number"
num.toFixed(0) == "3"
Ответ 6
var i = parseInt(n, 10);
Если вы не укажете, что значения radix, такие как '010'
, будут обрабатываться как восьмеричные (и поэтому результат будет 8
not 10
).
Ответ 7
"Лучший" способ зависит от:
Только после ответа на эти вопросы мы можем подумать о подходящих методах и скорости!
В спецификации ECMAScript 262: все числа (тип Number
) в javascript представлены/сохранены в:
" Формат IEEE 754 с двойной точностью плавающей точки (binary64)".
Таким образом, целые числа также представлены в одном формате с плавающей точкой (как числа без доли).
Примечание. Большинство реализаций используют, по возможности, более эффективные (для скорости и размера памяти) целые типы!Поскольку этот формат хранит 1 знаковый бит, 11 битов экспоненты и первые 53 значащих бита ( "мантисса" ), мы можем сказать, что только Number
-значения между -252
и +252
могут иметь долю.
Другими словами: все представляемые положительные и отрицательные значения Number
между 252
до (почти) 2(211/2=1024)
(в этот момент формат вызывает его в день Infinity
) уже являются целыми числами ( внутренне округленные, так как нет оставшихся бит для представления оставшихся дробных и/или наименее значимых целочисленных цифр).
И есть первая "добыча":
Вы не можете управлять внутренним режимом округления Number
-результатов для встроенных потоков Literal/String для конвертирования по платам (округление: IEEE 754-2008 "раунд до ближайшего, привязывается к четному" ) и встроенная арифметика операции (округление: IEEE 754-2008 "от ближайшего к ближайшему" ).
Например:
252+0.25 = 4503599627370496.25
округляется и сохраняется как: 4503599627370496
252+0.50 = 4503599627370496.50
округляется и сохраняется как: 4503599627370496
252+0.75 = 4503599627370496.75
округляется и сохраняется как: 4503599627370497
252+1.25 = 4503599627370497.25
округляется и сохраняется как: 4503599627370497
252+1.50 = 4503599627370497.50
округляется и сохраняется как: 4503599627370498
252+1.75 = 4503599627370497.75
округляется и сохраняется как: 4503599627370498
252+2.50 = 4503599627370498.50
округляется и сохраняется как: 4503599627370498
252+3.50 = 4503599627370499.50
округляется и сохраняется как: 4503599627370500
Для управления округлением вашей Number
требуется дробная часть (и хотя бы один бит для ее представления), в противном случае ceil/floor/trunc/near возвращает целое число, которое вы ему подавали.
Чтобы правильно пополнить/поместить/урезать число до x значащих дробных десятичных цифр, нам остается только, если соответствующее минимальное и наименьшее дробное значение по-прежнему будет давать двоичное дробное значение после округления (так что не быть потолком или перекрывается до следующего целого числа).
Так, например, если вы ожидаете "правильного" округления (для ceil/floor/trunc) до 1 значительной дробной десятичной цифры (x.1 to x.9
), нам нужно как минимум 3 бита (не 4), чтобы дать нам двоичное дробное значение:
0.1
ближе к 1/(23=8)=0.125
, чем к 0
, а 0.9
ближе к 1-1/(23=8)=0.875
, чем к 1
.
только до ±2(53-3=50)
все представляемые значения имеют ненулевую двоичную дробь не более первой значащей десятичной дробной цифры (значения x.1
- x.9
).
Для 2 десятичных знаков ±2(53-6=47)
для 3 десятичных знаков ±2(53-9=44)
для 4 десятичных знаков ±2(53-13=40)
для 5 десятичных знаков ±2(53-16=37)
для 6 десятичных знаков ±2(53-19=34)
для 7 десятичных знаков ±2(53-23=30)
для 8 десятичных знаков ±2(53-26=27)
для 9 десятичных знаков ±2(53-29=24)
, для десяти десятичных знаков ±2(53-33=20)
, для 11 десятичных знаков ±2(53-36=17)
и т.д.
A "Безопасное целое число" в javascript - целое число:
- который может быть точно представлен как число двойной точности IEEE-754, и
- чье представление IEEE-754 не может быть результатом округления любого другого целого числа в соответствии с представлением IEEE-754
(даже если ±253
(в виде точной мощности 2) может быть точно представлен, он не является безопасным целым числом, поскольку он также мог бы быть ±(253+1)
, прежде чем он был округлен, чтобы вписаться в максимум 53 самых значительных бит).
Это эффективно определяет диапазон подмножества (безопасно представимых) целых чисел между -253
и +253
:
- от:
-(253 - 1) = -9007199254740991
(включительно)
(константа, предоставленная как статическое свойство Number.MIN_SAFE_INTEGER
с ES6)
-
to: +(253 - 1) = +9007199254740991
(включительно)
(константа, предоставленная как статическое свойство Number.MAX_SAFE_INTEGER
с ES6)
Тривиальный polyfill для этих 2 новых констант ES6:
Number.MIN_SAFE_INTEGER || (Number.MIN_SAFE_INTEGER=
-(Number.MAX_SAFE_INTEGER=9007199254740991) //Math.pow(2,53)-1
);
Поскольку ES6 также имеет бесплатный статический метод Number.isSafeInteger()
, который проверяет, имеет ли переданное значение тип Number
и является целым числом в пределах безопасного целочисленный диапазон (возврат булевых true
или false
).
Примечание: также будет возвращен false
для: NaN
, Infinity
и, очевидно, String
(даже если он представляет число).
Polyfill пример:
Number.isSafeInteger || (Number.isSafeInteger = function(value){
return typeof value === 'number' &&
value === Math.floor(value) &&
value < 9007199254740992 &&
value > -9007199254740992;
});
ECMAScript 2015/ES6 предоставляет новый статический метод Math.trunc()
для усечения float в целое число:
Возвращает неотъемлемую часть числа x, удаляя любые дробные числа. Если x уже является целым числом, результатом будет x.
Или добавьте проще (MDN):
В отличие от других трех математических методов: Math.floor()
, Math.ceil()
и Math.round()
, способ Math.trunc()
работает очень просто и просто:
просто усечь точку и цифры за ней, независимо от того, является ли аргумент положительным числом или отрицательным числом.
Далее мы можем объяснить (и polyfill) Math.trunc()
следующим образом:
Math.trunc || (Math.trunc = function(n){
return n < 0 ? Math.ceil(n) : Math.floor(n);
});
Обратите внимание, что вышеприведенная полезная нагрузка polyfill потенциально может быть лучше оптимизирована движком по сравнению с:
Math[n < 0 ? 'ceil' : 'floor'](n);
Использование: Math.trunc(/* Number or String */)
Вход: (целая или плавающая точка) Number
(но с удовольствием попробуем преобразовать строку в число)
Вывод: (Целое число) Number
(но с удовольствием попробуем преобразовать Number в String в string-context)
Диапазон: -2^52
до +2^52
(помимо этого мы должны ожидать "округления-ошибки" (и в какой-то момент научной/экспоненциальной нотации) просто и просто потому, что наш вход Number
в IEEE 754 уже потерянная дробная точность: поскольку числа от ±2^52
до ±2^53
являются уже внутренне округленными целыми числами (например, 4503599627370509.5
внутренне уже представлено как 4503599627370510
), а за пределами ±2^53
целые числа также теряют точность (полномочия 2)),
Преобразование с плавающей точкой в целое, вычитая Remainder (%
) из раздела 1
:
Пример: result = n-n%1
(или n-=n%1
)
Это также должно быть усекать. Поскольку оператор Remainder имеет более высокий precedence, чем вычитание, мы эффективно получаем: (n)-(n%1)
.
Для положительных чисел легко видеть, что на этих этажах значение: (2.5) - (0.5) = 2
,
для отрицательных чисел это имеет значение: (-2.5) - (-0.5) = -2
(потому что --=+
so (-2.5) + (0.5) = -2
).
Поскольку ввод и вывод Number
, мы должны получить тот же полезный диапазон и вывод по сравнению с ES6 Math.trunc()
(или it polyfill). < ш > Примечание: жестко я боюсь (не уверен), могут быть различия: потому что мы делаем арифметику (которая внутренне использует режим округления "nearTiesEven" (например, округление Banker)) на исходном номере (float) и втором производном номере (фракция), это, кажется, приглашает компаундирование digital_representation и арифметические ошибки округления, таким образом, потенциально возвращая поплавок в конце концов..
Преобразование float to integer с помощью (ab-) с помощью побитовых операций:
Это работает путем принудительного преобразования (с плавающей запятой) Number
(усечения и переполнения) в подписанное 32-битное целочисленное значение (два дополнения) с использованием побитовой операции на Number
(и результат преобразуется назад к (с плавающей запятой) Number
, которая содержит только целочисленное значение).
Опять, ввод и вывод Number
(и снова тихая конвертация из String-input в Number и Number-output в String).
Более важные жесткие (и обычно забытые и не объясненные):
в зависимости от побитовой операции и знака числа, полезный диапазон будет ограничен между:
-2^31
до +2^31
(например, ~~num
или num|0
или num>>0
) ИЛИ 0
до +2^32
(num>>>0
).
Это должно быть дополнительно разъяснено следующей справочной таблицей (содержащей все "критические" примеры):
n | n>>0 OR n<<0 OR | n>>>0 | n < 0 ? -(-n>>>0) : n>>>0
| n|0 OR n^0 OR ~~n | |
| OR n&0xffffffff | |
----------------------------+-------------------+-------------+---------------------------
+4294967298.5 = (+2^32)+2.5 | +2 | +2 | +2
+4294967297.5 = (+2^32)+1.5 | +1 | +1 | +1
+4294967296.5 = (+2^32)+0.5 | 0 | 0 | 0
+4294967296 = (+2^32) | 0 | 0 | 0
+4294967295.5 = (+2^32)-0.5 | -1 | +4294967295 | +4294967295
+4294967294.5 = (+2^32)-1.5 | -2 | +4294967294 | +4294967294
etc... | etc... | etc... | etc...
+2147483649.5 = (+2^31)+1.5 | -2147483647 | +2147483649 | +2147483649
+2147483648.5 = (+2^31)+0.5 | -2147483648 | +2147483648 | +2147483648
+2147483648 = (+2^31) | -2147483648 | +2147483648 | +2147483648
+2147483647.5 = (+2^31)-0.5 | +2147483647 | +2147483647 | +2147483647
+2147483646.5 = (+2^31)-1.5 | +2147483646 | +2147483646 | +2147483646
etc... | etc... | etc... | etc...
+1.5 | +1 | +1 | +1
+0.5 | 0 | 0 | 0
0 | 0 | 0 | 0
-0.5 | 0 | 0 | 0
-1.5 | -1 | +4294967295 | -1
etc... | etc... | etc... | etc...
-2147483646.5 = (-2^31)+1.5 | -2147483646 | +2147483650 | -2147483646
-2147483647.5 = (-2^31)+0.5 | -2147483647 | +2147483649 | -2147483647
-2147483648 = (-2^31) | -2147483648 | +2147483648 | -2147483648
-2147483648.5 = (-2^31)-0.5 | -2147483648 | +2147483648 | -2147483648
-2147483649.5 = (-2^31)-1.5 | +2147483647 | +2147483647 | -2147483649
-2147483650.5 = (-2^31)-2.5 | +2147483646 | +2147483646 | -2147483650
etc... | etc... | etc... | etc...
-4294967294.5 = (-2^32)+1.5 | +2 | +2 | -4294967294
-4294967295.5 = (-2^32)+0.5 | +1 | +1 | -4294967295
-4294967296 = (-2^32) | 0 | 0 | 0
-4294967296.5 = (-2^32)-0.5 | 0 | 0 | 0
-4294967297.5 = (-2^32)-1.5 | -1 | +4294967295 | -1
-4294967298.5 = (-2^32)-2.5 | -2 | +4294967294 | -2
Примечание 1: последний столбец имеет расширенный диапазон от 0
до -4294967295
с помощью (n < 0 ? -(-n>>>0) : n>>>0)
.
Примечание 2: побитовое представляет свои собственные служебные данные об изменении (серьезность vs Math
зависит от фактической реализации, поэтому побитовое может быть быстрее (часто в старых исторических браузерах)).
Очевидно, что если ваш номер с плавающей запятой был String
для начала,
parseInt(/*String*/, /*Radix*/)
был бы подходящим выбором для синтаксического анализа его в целое число Number
.
parseInt()
также усекает (для положительных и отрицательных чисел).
Диапазон снова ограничивается плавающей точкой с двойной точностью IEEE 754, как описано выше для метода Math
.
Наконец, если у вас есть String
и ожидание вывода String
, вы также можете нарезать точку и дробь радиуса (что также дает более высокий точный диапазон усечения по сравнению с плавающей точкой с двойной точностью IEEE 754 (±2^52
))
EXTRA:
Из приведенной выше информации вы должны иметь все, что вам нужно знать.
Если вы хотите, чтобы округлился от нуля (aka round to бесконечность), вы можете изменить Math.trunc()
polyfill, например:
Math.intToInf || (Math.intToInf = function(n){
return n < 0 ? Math.floor(n) : Math.ceil(n);
});
Math.intToInf || (Math.intToInf = function(n){
return n < 0 ? Math.floor(n) : Math.ceil(n);
});
Ответ 8
Использование побитовых операторов. Это может быть не самый ясный способ преобразования в целое число, но он работает с любым типом данных.
Предположим, что ваша функция принимает аргумент value
, и функция работает таким образом, что value
всегда должна быть целым числом (и принимается 0). Тогда любое из следующего присваивает value
как целое число:
value = ~~(value)
value = value | 0;
value = value & 0xFF; // one byte; use this if you want to limit the integer to
// a predefined number of bits/bytes
Лучшая часть заключается в том, что это работает со строками (что вы можете получить из текстового ввода и т.д.), которые являются номерами ~~("123.45") === 123
. Любые не числовые значения приводят к 0
, т.е.
~~(undefined) === 0
~~(NaN) === 0
~~("ABC") === 0
Он работает с шестнадцатеричными числами как строки (с префиксом 0x
)
~~("0xAF") === 175
Полагаю, есть какое-то принуждение к типу. Я проведу некоторые тесты производительности, чтобы сравнить их с parseInt()
и Math.floor()
, но мне нравится иметь дополнительное удобство без Errors
, которое бросается и получает 0
для не-чисел
Ответ 9
Итак, я сделал тест на Chrome
, когда ввод уже является числом, самым быстрым будет ~~num
и num|0
, половина скорости: Math.floor
, а самая медленная - parseInt
см. здесь
РЕДАКТИРОВАТЬ: похоже, есть еще один человек, который сделал округление (больше результата) и дополнительных способы: num>>0
(быстрее, чем |0
) и num - num%1
(иногда быстро)
Ответ 10
Вопрос заключается в том, что речь идет конкретно о преобразовании из float в int. Я понимаю, что способ сделать это - использовать toFixed
. Так что...
var myFloat = 2.5;
var myInt = myFloat.toFixed(0);
Кто-нибудь знает, если Math.floor()
более или менее исполнен, чем Number.toFixed()
?
Ответ 11
вы также можете сделать это следующим образом:
var string = '1';
var integer = a * 1;
Ответ 12
parseInt(), вероятно, лучший. a | 0
не выполняет то, что вам действительно нужно (он просто присваивает 0, если a является undefined или нулевым значением, что означает, что пустой объект или массив проходит тест), а Math.floor работает с помощью какого-то трюка (it в основном вызывает parseInt() в фоновом режиме).