Как определить, находится ли 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;
}

По сути, то, что я делаю, это:

  1. Обернуть DataView около 4 байт ArrayBuffer,
  2. Хранить Number значение в виде 32-разрядного числа с плавающей точкой в ​​буфере.
  3. Получить преобразованный номер обратно из буфера.
  4. Рассчитайте относительную погрешность между исходным значением и преобразованным значением.
  5. Проверьте, меньше ли относительная ошибка, чем у машинного эпсилона для чисел с плавающей запятой, используя Number.EPSILON,

Пара комментариев:

Приведенная выше логика звучит для того, чего я пытаюсь достичь? Это излишне? Есть ли более идиоматичный, элегантный или эффективный способ сделать это?

Обновить

Тем временем я выяснил, что вышеуказанный подход неверен. Это не удастся для широкого диапазона значений, таких как 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);
}
Другие вопросы по тегам