1. 44
  1. 15

    The direct problem here is that JavaScript’s only numeric type is a 64-bit float, which has a 53 bit mantissa, so it can’t represent all 64-bit integers.

    JSON is not JavaScript; the JSON spec (ECMA-404) only specifies the syntax (grammar) of numbers, not nothing about range or precision. So in theory all bets are off when numbers get passed in and out of JSON. I think in practice 64-bit floats are a lowest common denominator.

    JSON parsers vary in how they represent the numbers they output. Objective-C and Swift tend to use NSNumber, which is a sort of union type that can store 64-but ints or doubles. I remember Go’s parser initially converted numbers to doubles but later added the option to preserve large ints. Some parsers can preserve all numbers as strings for perfect round trip fidelity.

    (There are similar problems with JSON objects. In JavaScript the order of keys is significant, but not in JSON. And many parsers output objects as hash tables, which are unordered. Still, I’ve run into some JSON based formats that insist on assigning meaning to the order of keys.)

    1. 8

      Good reason to use uuids! Can just treat them as strings and avoid this class of issue :-)

      1. 1

        Yup. Or just encode the int as a string.

        1. 5

          Problem with encoding an int like that is that you need to remember to do it explicitly, and because this is the sort of problem that works most of the time, it’s too easy to not notice one. Uuids OTOH will tend towards being default just regarded as a opaque string without explicit work, and so are more likely to default to a safe interpretation.

      2. 5

        Literally happened to me with random u64 values passed through rust serde‘s json serialization. I worked around it with numbers stored as strings but was eventually able to use counters instead of u64. Was fun to debug.

        1. 2

          Ran into this problem in an excel spreadsheet, which only supports fifteen digits of precision.

          1. 2

            I once wondered why some Google API returned structures like {'lo': 123, 'hi': 0} instead of just the number 123, and realized that the numbers probably can grow beyond 2**53.

            1. 1

              Only legacy systems should have any issue with this, right? BigInt exists now and should be used for anything that can approach the limits of 2^53.

              1. 1

                json only defines one number type; you don’t get to serialize or parse as bigint.

                1. 1

                  text. you should know the fields you’re parsing and how to parse them.

              2. 1

                Been there, did that; in 2013: https://github.com/SBJson/SBJson/pull/171#issuecomment-19842731

                Objective-C’s NSDecimalNumber class not actually being a BigNum tricked me for a long while :-)

                1. 1

                  The only parser I’ve seen that handles big numbers in JSON is OCaml’s Yojson with its Yojson.Safe module. The numbers that it can’t represent with OCaml’s int type (a 31 or 63 bit integer, depending on the architecture) it just returns as strings with a separate Intlit variant.