0

I have a backend that sends numbers with up to 19 digits before the decimal point and 6 digits after the decimal point. The backend is exact and correctly converts these digits into JSON like

{
  myNumber: 9999999999999999999.123456
}

On the frontend I use JavaScript and the standard fetch API response.json().

  1. By default the myNumber is converted into JavaScripts number type by response.json(). Can there be any loss of precision during this conversion?
  2. Later on I might convert the number type back to string. Can there be any loss of precision during this conversion?

I need to understand which numbers can be safely converted from (a) the JSON representation to (b) JavaScripts number type and (c) back to a string. In which ranges will (a) and (c) be identical, when will I run into issues?

I hope I abstracted that enough away, but in case anyone is interested: The backend is C# Web API with SQL Server which uses DECIMAL(19,6).

stefan.at.kotlin
  • 15,347
  • 38
  • 147
  • 270
  • A simple test in the console in Chrome showed that doing: `9999999999999999999.123456.toString();` produced -> `"10000000000000000000"` and doing `new Number("9999999999999999999.123456")` produced `10000000000000000000` – Ryan Wilson May 14 '21 at 17:31
  • @RyanWilson well `9999999999999999999 > Number.MAX_SAFE_INTEGER` is `true`, so it's hardly surprising. – VLAZ May 14 '21 at 17:38
  • 1
    @VLAZ Thanks? I was just pointing out that this can be easily tested. I don't think this post was necessary. – Ryan Wilson May 14 '21 at 17:40
  • @RyanWilson evidently OP did not consider it. Teaching how to f̶i̶s̶h̶ check *any* number for safety seemed relevant alongside giving them f̶i̶s̶h̶ the information that this one number is not safe. – VLAZ May 14 '21 at 17:44
  • I want to thank you both, I didn't think of the test by Ryan and I wasn't aware of the `Number.MAX_SAFE_INTEGER` :-) – stefan.at.kotlin May 14 '21 at 17:45
  • Related: https://stackoverflow.com/questions/67538240/which-numbers-can-safely-be-converted-into-javascript-numeric-type – Peter O. May 14 '21 at 17:46

1 Answers1

3

Basically, use strings instead of JSON numbers. RFC 8259 states:

This specification allows implementations to set limits on the range and precision of numbers accepted. Since software that implements IEEE 754 binary64 (double precision) numbers [IEEE754] is generally available and widely used, good interoperability can be achieved by implementations that expect no more precision or range than these provide, in the sense that implementations will approximate JSON numbers within the expected precision. A JSON number such as 1E400 or 3.141592653589793238462643383279 may indicate potential interoperability problems, since it suggests that the software that created it expects receiving software to have greater capabilities for numeric magnitude and precision than is widely available.

The numbers you're dealing with have significantly more digits than IEEE-754 double precision will preserve.

If you use a string in the JSON like this:

{
    "myNumber": "9999999999999999999.123456"
}

... then you can handle the conversion to/from any high/arbitrary-precision type yourself; it's completely in your control.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194