Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Exactly. JSON Number ARE NOT ACTUAL NUMBERS. They're really restricted strings (or, as you quote, a syntax for representing numbers).

IMO this wasn't originally a bad thing at all. There are so many different types of numbers, with so many different behaviours (does `1` == `1.0`? Not in statistics class) that trying to work it all out in JSON would have been a fools errand. The problem is that so many JSON parsers are overly aggressive about turning JSON Numbers into something specific (usually floats) that when writing a JSON API we have to assume it will be consumed in this lowest-common-denomitor way.

By the way, I don't think that all JSON numbers are technically both integers and floats. `42` and `42.0` are clearly different according to the spec, IMO. I'd be curious to get your thoughts on this.



The spec is too vague; it's "agnostic". A number can include a fractional part, but it doesn't say if that means that fractionless numbers are to be treated as integers. It leaves that to the implementation, which is a terrible idea for an interchange format.

Ruby, IMHO, errs on the side of consistency (fractionless numbers become Fixnum, so encode(decode("1")) => "1"), whereas Go goes the opposite way (all numbers become float64, so encode(decode("1")) => "1.0", unless you enable the magic json.Number type).

JavaScript is an interesting scenario, because JS doesn't even have integer numbers, which means it doesn't have any choice in the matter. Interestingly, Node.js/V8 truncates the fraction if it can: JSON.dump(JSON.parse("1.0")) => "1".

It's a mess.


Thanks for your analysis (and the Ruby and JS reports). The more I think about it the more I think parsers should not parse to numbers by default, but instead to (using a Haskell-based pseudo type):

  data Sign = Positive | Negative

  data Digit = Zero | One ... Eight | Nine

  data JSONNumber = JSONNumber
    { _sign           :: Maybe Sign
    , _integerPart    :: [Digit]
    , _fractionalPart :: Maybe [Digit]
    }
Actually this should include exponents as well -- basically parsers should just report exactly what the spec allows, without coercing to float or whatever.

Of course, users of the parsers could ask to have a number converted to a float, but that wouldn't be the default.

We're a long way from that though. At the moment I don't even feel like I can write a protocol that expected implementations to distinguish `1` and `1.0`. This should not be the case.


> At the moment I don't even feel like I can write a protocol that expected implementations to distinguish `1` and `1.0`.

Indeed you can't, among other reasons because that distinction doesn't exist in JavaScript, but ... why would you want to make such a subtle distinction?

If you really want what you described above, you can get it by using a string.


I respect the pragmatism of your reply. At the same time "distinguish decimals and integers" is about as far from a subtle distinction as you can get.


The distinction may be familiar to us, but if you ask someone on the street, "1" and "1.0" are the same number. The habit we have of giving them different types is somewhat arbitrary.


It sounds like what you're saying is that it should be deserialized as Java's BigDecimal, or whatever the equivalent of that is in a given/language framework; and if there isn't one, then the JSON parser should provide that equivalent.


> JSON Number ARE NOT ACTUAL NUMBERS.

Well, no. They're numbers as humans think about them.

(Cue the saying about programming being a job where you hate yourself for not thinking enough like a computer)


Almost two weeks late, but I thought about this more and I totally agree with you.

I should have said "JSON number's aren't machine numbers" (e.g. float, double, integer). But serialized, human-readable numbers are still numbers, so I was wrong.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: