Closed qntm closed 7 years ago
JavaScript numbers are allowed as inputs to bigInt
primarily for convenience when using small numbers. I can't think of any reasonable use case for a user wanting to pass in a number too large to be represented by Number
.
In JavaScript the numbers 999999999999999000000
, 999999999999999000034
, and 999999999999998951424
are indistinguishable. For example 999999999999998951424 === 999999999999999000034
returns true. Once they're passed into bigInteger
it's impossible to recover the original form, so any one of these (among thousands of other options) is an equally valid interpretation.
In general, this library tries to maintain parity with the behavior of JavaScript to avoid unexpected inconsistencies, so matching the behavior of toString
seems to make the most sense to me.
It would be a surprise for users if bigInt(x).toString()
did not match x.toString()
.
In general I believe that if
Number.isInteger(x)
istrue
,bigInt(x).toJSNumber() === x
should also betrue
, since the big integers are a superset of the little ones.
Can you give an example where this does not hold? Using the example you gave, bigInt(999999999999999000034).toJSNumber() === 999999999999999000034
evaluates to true
.
Ah, so bringing up that last assertion about things which should be true
was a mistake on my part. It doesn't relate to the problem I'm describing, I shouldn't have brought it up. I have removed it from what I originally wrote. Sorry!
Anyway, my use case was that I wanted to know what the exact value of Number.MAX_VALUE
was, and maybe do some arithmetic with it, so I did bigInt(Number.MAX_VALUE).toString()
and what I got was
'179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
The actual value of Number.MAX_VALUE
is 21024 - 2971:
179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368
This discrepancy caused some later calculations to fail. It took me a while to trace the reason why.
I discovered this because I am putting together an API which accepts both JavaScript numbers and bigInteger
s. When a user supplies me with a JavaScript number, I convert it to a bigInteger
, under the assumption that the value that the user has provided is exact. However, big-integer
introduces an error at this stage.
When you say "In JavaScript the numbers 999999999999999000000, 999999999999999000034, and 999999999999998951424 are indistinguishable", I would argue that JavaScript simply does not have the numbers 999999999999999000000 or 999999999999999000034. It only has 999999999999998951424. Arithmetic using 999999999999998951424 should behave as such, including big-integer arithmetic.
I understand what you're getting at, but I'm hesitant to make this change for a number of reasons.
bigInt(x).toString()
and x.toString()
.bigInt(999999999999999000034).toString()
and see the result "999999999999999000000"
, it's straightforward to understand what's going on behind the scenes and realize that the number passed in was truncated because it's too large to be represented accurately with a native JavaScript number.
After seeing bigInt(999999999999999000000).toString()
result in "999999999999998951424"
, most people would think "wtf is going on?!".toString
method which typically just redirects to the native JavaScript toString
. Performance considerations are relatively important for toString
since it is one of the most commonly called methods.This would be a change at bigInt
construction time rather than at stringification time. Does that change the performance considerations?
Throughout this example code, the value of
x
is precisely 999999999999998951424, since this is the nearest available float to the number which was asked for by the source code. We see at the second line that, when printingx
out, JavaScript only supplies the bare minumum number of decimal places required to uniquely specify a float with the same value, however, this is a big mislead. At the third line, we demonstrate that, for arithmetical purposes, which is what we care about,x
is 999999999999998951424.I think
big-integer
should likewise favour the arithmetical meaning of a number, but:It looks as if we favour the lossy JavaScript string representation over the actual number which
x
represents.[some stuff removed here]
The resolution would be to inspect
x
more carefully to determine its IEEE754 radix and mantissa, then use these to construct abig-integer
object from it.Of course, this would be a change from existing behaviour, which possibly is already expected by users...