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, i.e. 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:
DataView
around a 4 byte ArrayBuffer
.Number
value as a 32 bit float in the buffer.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 (224 and -(224)).
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 (i.e. not an Inf or NaN). In that case,
function isfloat32(x) {
return isFinite(x) && x == Math.fround(x);
}