获取JavaScript编号的精确十进制表示

时间:2017-07-09 16:28:55

标签: javascript

JavaScript中的每个有限数字都具有确切的实际值。例如:

const x = Number.MAX_VALUE

此处,x的精确值为2 1024 - 2 971 =

  

179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368

我们可以通过在算术中使用x来证明这一点:

console.log(x % 10000) // 8368

但是如何获得那些十进制数字的全部

我喜欢它,如果解决方案也适用于非整数,例如const y = Number.EPSILON正好是2 -52 =

  

0.0000000000000002220446049250313080847263336181640625

2 个答案:

答案 0 :(得分:2)

我发现这样做的唯一有效方法是

  • 将该号码写入Float64Array
  • 然后使用按位操作
  • 读取符号,指数和尾数
  • 将符号,指数和尾数传递给任意精度库,例如decimal.js
  • 从那里串起来。

例如:

const Decimal = require('decimal.js')

Decimal.set({
  precision: 10000
})

const stringify = number => {
    if (Object.is(-0, number)) {
        return '-0'
    }

    const uint8Array = new Uint8Array(new Float64Array([number]).buffer)

    const sign = ((uint8Array[7] & 0b10000000) >> 7)

    const exponent =
        ((uint8Array[7] & 0b01111111) << 4) +
        ((uint8Array[6] & 0b11110000) >> 4)

    // Can't use bit shifts here because JS bitwise operations are 32-bit
    const mantissa =
        ((uint8Array[6] & 0b00001111) * Math.pow(1 << 8, 6)) +
        ((uint8Array[5] & 0b11111111) * Math.pow(1 << 8, 5)) +
        ((uint8Array[4] & 0b11111111) * Math.pow(1 << 8, 4)) +
        ((uint8Array[3] & 0b11111111) * Math.pow(1 << 8, 3)) +
        ((uint8Array[2] & 0b11111111) * Math.pow(1 << 8, 2)) +
        ((uint8Array[1] & 0b11111111) * Math.pow(1 << 8, 1)) +
        ((uint8Array[0] & 0b11111111) * Math.pow(1 << 8, 0))

    const one = new Decimal(-1).toPower(sign)
    const power = new Decimal(2).toPower((exponent === 0 ? 1 : exponent) - 1023)
    const fraction = new Decimal(mantissa).dividedBy(Math.pow(2, 52)).plus(exponent === 0 ? 0 : 1)

    return one.times(power).times(fraction).toFixed()
}

console.log(stringify(Number.MAX_VALUE))

输出:

  

179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368

类似地:

console.log(stringify(Number.EPSILON))

输出:

  

0.0000000000000002220446049250313080847263336181640625

请注意,new Decimal(Number.MAX_VALUE).toFixed() 不会返回所需的结果。我也遇到了BigInteger.js这个问题。信息在大量施工时丢失。这可以说是两个图书馆的缺陷。我没有找到没有这个缺陷的库。

此外,虽然decimal.js可以表示负零,但它似乎不喜欢输出前导一元否定符号,所以特别容易这样做。

答案 1 :(得分:-2)

你误解了浮点。

你得到的最大值约为14位,因为没有精确度。 2 ^ 52

最大数字是一个包含三个部分的数字。

  • 符号,正面或负面,是1位。因此你得到0和-0
  • 分数52位,精度约为14位。
  • 指数11位,是一个有符号值,是将Fraction值乘以的数量。 2 ^指数

有关详细信息,请参阅wiki Double-precision_floating-point_format