Last active
May 21, 2025 10:55
-
-
Save qntm/5c3d90cb3c5673f8042651dca753991d to your computer and use it in GitHub Desktop.
Get all the decimal digits of any JavaScript float
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const SIGN_BITS = 1n | |
const EXPONENT_BITS = 11n | |
const MANTISSA_BITS = 52n | |
const BIAS = 1023n | |
export const stringify = value => { | |
if (typeof value !== 'number') { | |
throw Error('Not a number') | |
} | |
const dataView = new DataView(new ArrayBuffer(8)) | |
dataView.setFloat64(0, value) | |
const bigUint64 = dataView.getBigUint64(0) | |
const mantissaBits = (bigUint64 >> 0n) & ((1n << MANTISSA_BITS) - 1n) | |
const exponentBits = (bigUint64 >> MANTISSA_BITS) & ((1n << EXPONENT_BITS) - 1n) | |
const signBits = (bigUint64 >> (MANTISSA_BITS + EXPONENT_BITS)) & ((1n << SIGN_BITS) - 1n) | |
const sign = signBits === 0b0n ? '' : '-' | |
if (exponentBits === ((1n << EXPONENT_BITS) - 1n)) { | |
if (mantissaBits === 0n) { | |
return sign + 'Infinity' | |
} | |
return 'NaN' | |
} | |
const isSubnormal = exponentBits === 0b0n | |
// So as to keep this in integers, multiply the fraction by 2 ** 52 while subtracting | |
// that same power from the exponent | |
const m = ((isSubnormal ? 0n : 1n) << MANTISSA_BITS) + mantissaBits | |
const e = (isSubnormal ? 1n : exponentBits) - BIAS - MANTISSA_BITS | |
if (e >= 0n) { | |
// Pure integers, no problem | |
return sign + String(m << e) | |
} | |
// Multiply by a large enough power of 10 that all possible decimal digits are preserved | |
// when we then divide by the power of 2 | |
const power10 = 10n ** -e | |
const f = (m * power10) >> -e | |
const pre = f / power10 | |
const post = f % power10 | |
if (post === 0n) { | |
return sign + String(pre) | |
} | |
return sign + String(pre) + '.' + String(post).padStart(Number(-e), '0').replace(/0+$/, '') | |
} | |
export const parse = str => { | |
const value = Number(str) | |
if (stringify(value) === str) { | |
return value | |
} | |
throw Error('Parse failed') | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Examples: