Как определить, находится ли Javascript Number в диапазоне одинарной точности?
Согласно спецификации ECMAScript значения чисел Javascript соответствуют 64-битному двоичному формату IEEE 754 с двойной точностью.
Для валидатора WebIDL, над которым я сейчас работаю, мне нужно иметь возможность выяснить, можно ли преобразовать заданное числовое значение в тип с плавающей точкой WebIDL, т. Е. Представить его как 32-битное значение IEEE 754 конечной одинарной точности с конечной точностью,
В настоящее время я остановился на следующем подходе:
validate: function(value) {
if (typeof value !== 'number' || !Number.isFinite(value)) {
return false;
}
if (value === 0) {
return true;
}
var view = new DataView(new ArrayBuffer(4));
view.setFloat32(0, value);
var converted = view.getFloat32(0);
var relativeError = Math.abs(value - converted) / value;
return relativeError < Number.EPSILON;
}
По сути, то, что я делаю, это:
- Обернуть
DataView
около 4 байтArrayBuffer
, - Хранить
Number
значение в виде 32-разрядного числа с плавающей точкой в буфере. - Получить преобразованный номер обратно из буфера.
- Рассчитайте относительную погрешность между исходным значением и преобразованным значением.
- Проверьте, меньше ли относительная ошибка, чем у машинного эпсилона для чисел с плавающей запятой, используя
Number.EPSILON
,
Пара комментариев:
- Я реализую это как часть расширения Chrome, совместимость браузера на самом деле не проблема.
- Да, я прочитал то, что должен знать каждый компьютерщик об арифметике с плавающей точкой. Мои глаза еще не остановили кровотечение.
Приведенная выше логика звучит для того, чего я пытаюсь достичь? Это излишне? Есть ли более идиоматичный, элегантный или эффективный способ сделать это?
Обновить
Тем временем я выяснил, что вышеуказанный подход неверен. Это не удастся для широкого диапазона значений, таких как 1.001
, 3.14159
, и так далее. Изменение значения epsilon на epsilon машины для 32-разрядных операций с плавающей запятой (2–23), с другой стороны, слишком допустимо и допускает такие значения, как 16777217
,
Все еще ищу хорошее решение, но в настоящее время собираюсь использовать функцию ниже, которая просто проверяет целые верхние и нижние границы (2 24 и - (2 24)).
validate: function(value) {
return typeof value === 'number' && Number.isFinite(value)
&& value >= -16777216 && value <= 16777216;
}
1 ответ
Поскольку вы используете ECMAScript 6, вы можете использовать Math.fround
чтобы проверить, является ли это число с одинарной точностью:
function isfloat32(x) {
return isNaN(x) || x == Math.fround(x);
}
ОБНОВЛЕНИЕ: я пропустил часть о конечности с плавающей точкой WebIDL (то есть не Inf или NaN). В таком случае,
function isfloat32(x) {
return isFinite(x) && x == Math.fround(x);
}