Skip to content

Instantly share code, notes, and snippets.

@subzey
Forked from 140bytes/LICENSE.txt
Created December 29, 2011 08:51
Show Gist options
  • Save subzey/1533031 to your computer and use it in GitHub Desktop.
Save subzey/1533031 to your computer and use it in GitHub Desktop.
getFloat64LE

getFloat64LE

Constructs a IEEE 754 64 bit float (double) from byte array. Handles both normal and subnormal values and special cases like ±∞, ±0 and NaN.

Usage

getFloat64LE(array, Math.pow)

array is a byte array. It may be Array, Arguments, ArrayBuffer, nodejs Buffer or any object that have keys 0 to 7. Values must be in range 0 to 255.

Math.pow is a Math.pow. Sorry, I had to do it in order to fit code into 140 bytes. See index-no-external-static.js for version that doesn't require it

More tweet sized code

See the 140byt.es site for a showcase of entries (built itself using 140-byte entries!), and follow @140bytes on Twitter.

To learn about byte-saving hacks for your own code, or to contribute what you've learned, head to the wiki.

140byt.es is brought to you by Jed Schmidt, with help from Alex Kloss. It was inspired by work from Thomas Fuchs and Dustin Diaz.

function(
a, // array of integers (0..255), bytes, little-endian
b, // placeholder: mantissa
c // placeholder: exponent
){
// Fetching mantissa (b)
for(
// Store lower 4-bit part of byte 6.
// Exponent (c) is unused yet, so it can be used as loop pointer
// It is the 6 either.
b = a[c = 6] % 16
;
// Continue while non-zero
c
;
// Store byte value at bytes 5 to 0 while shifting the mantissa.
// Mantissa cannot be shifted using bitwise operators as its
// value may (and most probably will) exceed Int32
b = a[--c] + b * 256
)
// Empty loop body
;
// Reuse c to store exponent
// Exponent will always fit into Int32
// Trim the highest bit of byte 7 by shifting it out of Int32 bounds
// then shifting it back to position 4.
// >>> operator cleans the sign
// Then adding upper 4-bit part of byte 6
c = a[7] << 25 >>> 21 | a[6] >> 4;
// Okay, we have mantissa and exponent, now return the product of...
return
// Sign of float (7th bit of 7th byte)
(a[7] >> 7 ? -1 : 1) *
// 2^(exponent - exponent bias - mantissa byte-length + 1)
// We cannot use 1075 as if exponent is zero Float64 underflows
// Save bytes by storing Math.pow, reuse `a` variable
(a = Math.pow)(2, c - 1074) *
// Fraction part
(c
// If exponent is not zero
? c^2047
// And if exponent if not 0x7FF
// Set the 53th bit of mantissa to 1
// and divide it by 2 (1 missed power of 2 in exponent)
// (b + Math.pow(2, 52)) * Math.pow(2, -1)
? b / 2 + a(2, 51)
// If exponent is 0x7FF the whole decoded value is a NaN
// or Infinity depending on mantissa
// 0/0 makes NaN, anything_else/0 makes Infinity
: !b / 0
/* <- end of ternary operator */
// If exponent is a zero, this is a subnormal number and
// we do nothing with mantissa
: b
/* <- end of ternary operator */
)
/* <- end of return statement */
}
function(a,b,c){for(b=a[c=6]%16;c;b=a[--c]+b*256);c=a[7]<<25>>>21|a[6]>>4;return(a[7]>>7?-1:1)*(a=Math.pow)(2,c-1074)*(c?c^2047?b/2+a(2,51):!b/0:b)}
function(a,z,b,c){for(b=a[c=6]%16;c;b=a[--c]+b*256);c=a[7]<<25>>>21|a[6]>>4;return(a[7]>>7?-1:1)*z(2,c-1074)*(c?c^2047?b/2+z(2,51):!b/0:b)}
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2011 subzey <[email protected]>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
{
"name": "getFloat64LE",
"description": "Constructs ECMA-262 Float64 (Double) from byte array",
"keywords": [
"float",
"double",
"float64",
"IEEE 754"
]
}
<!DOCTYPE html>
<head>
<title>Foo</title>
</head>
<body>
<table id="tests">
<thead>
<tr>
<th>Actual</th>
<th>Expected</th>
<th>Native ArrayBuffer</th>
<th>Comments</th>
</tr>
</thead>
</table>
<script>
var getFloat64LE = function(a,z,b,c){for(b=a[c=6]%16;c;b=a[--c]+b*256);c=a[7]<<25>>>21|a[6]>>4;return(a[7]>>7?-1:1)*z(2,c-1074)*(c?c^2047?b/2+z(2,51):!b/0:b)}
var nativeGetFloat64LE = function(a){
if (!window.ArrayBuffer || !window.DataView) return 'Not available for this browser';
var b = new ArrayBuffer(8);
for (var i=0; i<8; i++){
b[i] = a[i];
};
return new DataView(b).getFloat64(0, true /* LE flag */);
}
var testCases = [
{test: [0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xD5, 0x3F], expect: '0.3333333333333333', name: '+1/3'},
{test: [0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xD5, 0xBF], expect: '-0.3333333333333333', name: '-1/3'},
{test: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], expect: '0', name: 'zero'},
{test: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80], expect: '0', name: 'minus zero'},
{test: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x00], expect: '2.225073858507201e-308', name: 'Highest subnormal number'},
{test: [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80], expect: '-5e-324', name: 'Lowest subnormal number (Number.MIN_VALUE)'},
{test: [0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80], expect: '-1e-323', name: 'Second lowest subnormal number'},
{test: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7F], expect: '1.7976931348623157e+308', name: 'Highest finite number (Number.MAX_VALUE)'},
{test: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F], expect: 'Infinity', name: 'Infinity'},
{test: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF], expect: '-Infinity', name: 'Minus infinity'},
{test: [0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0xF0, 0xFF], expect: 'NaN', name: 'NaN'},
{test: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0x7F], expect: 'NaN', name: 'Another NaN'},
{test: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x40], expect: '42', name: 'The answer to Life, the Universe and Everything'}
];
var tbody = document.getElementById('tests').appendChild(document.createElement("tbody"));
for (var i=0; i<testCases.length; i++){
with (testCases[i]){
var data = [
getFloat64LE(test, Math.pow),
expect,
nativeGetFloat64LE(test),
name
]
};
var tr = tbody.appendChild(document.createElement('tr'));
for (var j=0; j<data.length; j++){
tr.appendChild(document.createElement('td')).appendChild(document.createTextNode(data[j]));
};
}
</script>
</body>
</html>
@tsaniel
Copy link

tsaniel commented Dec 30, 2011

This is awesome, @subzey!
Btw, what is the difference between index-no-external-static.js and index.js?

@subzey
Copy link
Author

subzey commented Dec 30, 2011

There's only one difference: in index.js version function gets Math.pow function as 2nd argument while no-external version doesn't.

Passing of static data via argument is allowed (as far as I understand), but personally I don't like this approach. I'd be happy if you and other golfers help me to save 8 bytes and fit "no-external" version into tweet size.

@tsaniel
Copy link

tsaniel commented Dec 31, 2011

What about replacing % 16 with & 15? It doesn't help save bytes though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment