简体   繁体   中英

How to determine if a Javascript Number is in single-precision range?

According to the ECMAScript specification , Javascript number values correspond a to double-precision 64-bit binary format IEEE 754 value.

For a WebIDL validator I'm currently working on, I need to be able to figure out if a given number value can be converted to a WebIDL float type, ie if it can be represented as a finite single precision 32-bit IEEE 754 value.

I've currently settled on the following approach:

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;
}

In essence, what I'm doing is:

  1. Wrap a DataView around a 4 byte ArrayBuffer .
  2. Store the Number value as a 32 bit float in the buffer.
  3. Get the converted number back out of the buffer.
  4. Calculate the relative error between the original value and the converted value.
  5. Check whether the relative error is smaller than the machine epsilon for floating point numbers, using Number.EPSILON .

A couple of comments:

Is the above logic sound for what I'm trying to achieve? Is it overkill? Is there a more idiomatic, elegant or performant way to do this?

Update

I have in the meantime figured out that the above approach is incorrect. It will fail for a wide range of values such as 1.001 , 3.14159 , and so on. Changing the epsilon value to the machine epsilon for 32-bit floats (2 −23 ) is on the other hand too permissive and allows values like 16777217 .

Still looking for a good solution, but currently going with the function below that just checks the integer upper and lower bounds (2 24 and -(2 24 )).

validate: function(value) {
    return typeof value === 'number' && Number.isFinite(value)
            && value >= -16777216 && value <= 16777216;
}

As you're using ECMAScript 6, you can use Math.fround to check if it's a single-precision number:

function isfloat32(x) {
  return isNaN(x) || x == Math.fround(x);
}

UPDATE: I missed the part about a WebIDL float being finite (ie not an Inf or NaN). In that case,

function isfloat32(x) {
  return isFinite(x) && x == Math.fround(x);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM