Read/Write bytes of float in JS
Is there any way I can read bytes of a float value in JS? What I need is to write a raw FLOAT or DOUBLE value into some binary format I need to make, so is there any way to get a byte-by-byte IEEE 754 representation?开发者_JAVA百科 And same question for writing of course.
You can do it with typed arrays:
var buffer = new ArrayBuffer(4);
var intView = new Int32Array(buffer);
var floatView = new Float32Array(buffer);
floatView[0] = Math.PI
console.log(intView[0].toString(2)); //bits of the 32 bit float
Or another way:
var view = new DataView(new ArrayBuffer(4));
view.setFloat32(0, Math.PI);
console.log(view.getInt32(0).toString(2)); //bits of the 32 bit float
Not sure what browser support is like though
I've created an expansion of Miloš's solution that should be a bit faster, assuming TypedArray
s are not an option of course (in my case I'm working with an environment where they're not available):
function Bytes2Float32(bytes) {
var sign = (bytes & 0x80000000) ? -1 : 1;
var exponent = ((bytes >> 23) & 0xFF) - 127;
var significand = (bytes & ~(-1 << 23));
if (exponent == 128)
return sign * ((significand) ? Number.NaN : Number.POSITIVE_INFINITY);
if (exponent == -127) {
if (significand == 0) return sign * 0.0;
exponent = -126;
significand /= (1 << 22);
} else significand = (significand | (1 << 23)) / (1 << 23);
return sign * significand * Math.pow(2, exponent);
}
Given an integer containing 4 bytes holding an IEEE-754 32-bit single precision float, this will produce the (roughly) correct JavaScript number value without using any loops.
Koolinc's snippet is good if you need a solution that powerful, but if you need it for limited use you are better off writing your own code. I wrote the following function for converting a string hex representation of bytes to a float:
function decodeFloat(data) {
var binary = parseInt(data, 16).toString(2);
if (binary.length < 32)
binary = ('00000000000000000000000000000000'+binary).substr(binary.length);
var sign = (binary.charAt(0) == '1')?-1:1;
var exponent = parseInt(binary.substr(1, 8), 2) - 127;
var significandBase = binary.substr(9);
var significandBin = '1'+significandBase;
var i = 0;
var val = 1;
var significand = 0;
if (exponent == -127) {
if (significandBase.indexOf('1') == -1)
return 0;
else {
exponent = -126;
significandBin = '0'+significandBase;
}
}
while (i < significandBin.length) {
significand += val * parseInt(significandBin.charAt(i));
val = val / 2;
i++;
}
return sign * significand * Math.pow(2, exponent);
}
There are detailed explanations of algorithms used to convert in both directions for all formats of floating points on wikipedia, and it is easy to use those to write your own code. Converting from a number to bytes should be more difficult because you need to normalize the number first.
I had a similar problem, I wanted to convert any javascript number to a Buffer and then parse it back without stringifying it.
function numberToBuffer(num) {
const buf = new Buffer(8)
buf.writeDoubleLE(num, 0)
return buf
}
Use example:
// convert a number to buffer
const buf = numberToBuffer(3.14)
// and then from a Buffer
buf.readDoubleLE(0) === 3.14
This works on current Node LTS (4.3.1) and up. didn't test in lower versions.
Would this snippet help?
var parser = new BinaryParser
,forty = parser.encodeFloat(40.0,2,8)
,twenty = parser.encodeFloat(20.0,2,8);
console.log(parser.decodeFloat(forty,2,8).toFixed(1)); //=> 40.0
console.log(parser.decodeFloat(twenty,2,8).toFixed(1)); //=> 20.0
I expect you could figure it out (blech), but I assume you're asking if there's something built-in. Not as far as I've ever heard; see sections 8.5 and 15.7 of the spec.
64-bit IEEE 754 float to its binary representation and back:
// float64ToOctets(123.456) -> [64, 94, 221, 47, 26, 159, 190, 119]
function float64ToOctets(number) {
const buffer = new ArrayBuffer(8);
new DataView(buffer).setFloat64(0, number, false);
return [].slice.call(new Uint8Array(buffer));
}
// octetsToFloat64([64, 94, 221, 47, 26, 159, 190, 119]) -> 123.456
function octetsToFloat64(octets) {
const buffer = new ArrayBuffer(8);
new Uint8Array(buffer).set(octets);
return new DataView(buffer).getFloat64(0, false);
}
// intToBinaryString(8) -> "00001000"
function intToBinaryString(i, length) {
return i.toString(2).padStart(8, "0");
}
// binaryStringToInt("00001000") -> 8
function binaryStringToInt(b) {
return parseInt(b, 2);
}
function octetsToBinaryString(octets) {
return octets.map((i) => intToBinaryString(i)).join("");
}
function float64ToBinaryString(number) {
return octetsToBinaryString(float64ToOctets(number));
}
function binaryStringToFloat64(string) {
return octetsToFloat64(string.match(/.{8}/g).map(binaryStringToInt));
}
console.log(float64ToBinaryString(123.123))
console.log(binaryStringToFloat64(float64ToBinaryString(123.123)))
console.log(binaryStringToFloat64(float64ToBinaryString(123.123)) === 123.123)
This is a slightly modified version this MIT-licensed code: https://github.com/bartaz/ieee754-visualization/blob/master/src/ieee754.js
精彩评论