Json ⊄ js
medium.com
medium.com
While swiftly coercing raw off-the-wire number representations to one's arbitrary precision library of choice can avoid most cases of noticeable accumulated error, it is irksome that the only way everyone seems to get by is by cross their fingers that any loss in precision caused by "JSON.parse" is meaningless to their application.
Or, the problem can be soundly avoided by using strings in the JSON payload, which is lame but effective and probably one's best practical choice. It is clearly an example of corruption spreading from Javascript to an otherwise reasonable feature of the data representation format JSON.
Up until 4-5 years ago, the EUR/USD or GBP/USD or GBP/EUR were quoted with 4 decimals; Nowadays, there are quite a few places where you have to deal with the 5th decimal to make the books balance. It's going to be a roundoff error either way, but it might be a round-off error that keeps been counters, auditors and therefore eventually IT people awake at night.
And then the market adopts a 5th digit - and you have to essentially rewrite all the math stuff.
If you're using language support (e.g., in VB until 2004 "dim cost as Money" was a declaration that was good for every purpose), then you're out of luck, and you do need a rewrite.
But even if you don't - this is far from trivial: Is your own implemented money type a fixed point type? If so, what is your fixed point? (e.g. Yen only needs 3 after the dot, but trillions in the front is not enough; GBP/USD needs 5 after the dot, but trillions usually IS enough).
Is it fixed per currency? Is it floating decimal point? The abstraction here is far from trivial if care about performance.
Of course, if you've used a language or a library with a usable decimal type (I'm sure there's other, but I've only been happy with Python), the abstraction has been taken care of.
But generally, abstracting money (value+currency) is not as simple as one would assume, and it is very rare that a production system gets it both right and future proof.
At that point screw it, 64.32 for 18.9 digits and round it to 128 bits with a currency code. Comes with built-in unit checking.
But it works well enough.
(Money 3 * 2 will return Money 6 if fromIntegral 2 = Money 2. But that is not really the operation you want to perform, so you might consider not implementing Num for Money.)
1. The speed gain is negligible for most programs
2. The addition of any pricing that requires fractional cents will require careful work to handle unit conversion to maintain integral representation.
#2 becomes ugly when one has an API many people use, and one must bother them to update their code paths to use the new, higher precision that can handle all money as integers. This is not even counting the case where one tries to cut a corner and someone ends up not doing the conversion they ought to.
Also, programs often are more lucid when operating in terms of the frequent units of choice, such as dollars or fractional dollars. Few domains price everything in cents by preference, because in aggregation often dollars -- sometimes many -- are exchanged. The problem gets worse if one needs weird units like 0.1c to regain integral numbers.
There are ways around these, but falling back on strings seems to me the lowest-maintenance option.
On the other hand, integral representations have few dependencies (a compliant javascript interpreter), which is also a pretty big plus for that.
Just about every practically used programming environment from every walk of life has such a thing in the standard library, and I suppose they are there for good reasons. However, the fact is that Javascript doesn't have one, so one will have to weigh dependencies into their decision.
Go - math/big
Java - BigDecimal
Python - decimal
Ruby - BigDecimal
PostgreSQL - numeric
Interesting mention: decimal in C#, deemed sufficient for monetary calculations at 128 bits of precision.
Another interesting mention: Oracle, which in my understanding handles just about all its numbers this way, by default. This might tell you something about their early customer base.
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
Math.pow(2, 53) === Math.pow(2, 53) - 1 // false
But I wonder why you write that for values in 1/100 one should multiply by 100! Wouldn't an IEEE double have no problems with values with two decimal points, too? Or are there many problems with that, e.g. that $0.01 + $0.01 might already cause rounding errors which do not even out by the clever IEEE rules?
Of course 2^54 woudln't be the highest value where one shouldn't worry anymore, but something smaller.
I only mention it because the yen thing surprised me once as a newbie programmer. (A tip: "%0.2f" is no substitute for actual i18n.) It never hurts to be aware of the edge cases.
I'm afraid it mine can only handle sea shells!
>I only mention it because the yen thing surprised me once as a newbie programmer.
So what's the yen thing? How does it differ?
[A bit of googling also turns up the "rin" (厘), which is a thousandth of a yen...]
The idea is that you have income for each loans, which then pay into bonds based on whatever rules may have been set. These rules often have terms like a fixed interest on the outstanding principal for senior bond issues, and then various divisions for the junior, with a weird "IO" piece that gets the leftovers that don't divide neatly. The rules can be anything that they were structured to be. The result surprisingly frequently is something where the allocation of the final penny in billions of dollars can be impacted by floating point ambiguity. (And the prospectus seldom will clarify this - the ultimate control lies in whatever the servicer's computer program does.)
EDIT: Millage taxes on property are denoted in mills—thousandths. The taxes are often fractional mills, like 2.225. So you need 10^-8 resulution at the least.
Interesting! I hadn't seen those before.
That strongly suggests that browsers ought to add some built-in extension types for arbitrary-precision arithmetic, using fast native libraries like GMP. That would then allow such JavaScript libraries to use the native types when available, and their existing pure-JavaScript support when not.
It doesn't look like http://es5.github.com/#Quote has anything to do with serializing to JSON string--am I missing something?
Plus its still just mega young. Look at legal systems, they've been around forever and it can be your life's work just to understand them at a competency to participate.
The miracle of software isn't that it works, it's that it does anything at all.
Yep, it only has strings either way.
> weak typing
Yep, it only has strings either way (also, "weak typing" does not mean jack shit, and talking about typing strength for a serialization format makes zero sense)
> barely any types in the first place
Yep, it only has one.
Things in XML do not need to be just strings.
PS: I still don't like XML, but your comment is technically incorrect.
Edit: Typo FIX.
Things in XML are just strings.
Schemas are metadata, annotations to tell processors "treat this string as [some other datatype]" (note how it's not going to work if you're not using the schema and a schema-aware processor?)
And guess what? Nothing stops you from doing exactly the same thing in JSON. In fact you don't have much of a choice for the datatypes JSON doesn't natively support (dates being the most common one, but not the only one by any mean). And good JSON interfaces provide for embedding transcodings directly in the parsing or dumping (that's what the `reviver` and `replacer` arguments do in JSON.parse and JSON.stringify) for exactly that purpose.
> PS: I still don't like XML, but your comment is technically incorrect.
Nope. Specific XML dialects may have non-string datatypes (XML-RPC certainly does), but XML only has strings. In the same way CSV only has strings, but specific CSV uses may have more. That's the plain facts of the matter.
XML and JSON are representations -- so they are all just bits. It's meaningless to say that, however.
The metadata and surrounding standards are what give those bits more meaning. So compare what the standards have to offer.
(Aside: I don't like XML and I think JSON is way over-hyped and under-delivers.)
Which I did. The XML standard only offers strings, and you can add schemas to JSON.
It's sad how many non-web developers still complain about how the Web is sour grapes.
> Some libraries implement an unsafe, but fast, JSON parse using “eval” for older browsers
eval is not fast! In fact it is the opposite of fast. Most JIT optimizations go away in the face of eval()! Do not use it even if you know it's safe. Use JSON.parse instead.
https://github.com/jquery/jquery/blob/master/src/core.js#L52...
Also, for the JSONP technique to work, it _has_ to be valid JavaScript so the escaping is necessary.
The validation code they use in case there is no native JSON implementation available is borrowed from Douglas Crockford's json2.js ( https://github.com/douglascrockford/JSON-js/blob/master/json... ) which was the inspiration for the native JSON implementations and should really be correct by now, both in terms of correctness but also circumventing regexp weaknesses in some engines.
Note that the code is now only a work-around for older browsers. Every modern browser supports native JSON parsing anyway.
<meta name='blah' content="<%= @data.to_json %>" />
However this has always seemed unclean to me. Does anyone else have a better, alternative method of inlining data? I'd rather not use inline scripts for the exact reason they mention.
<script type="text/json" id="mydata">
{ data: "..." }
</script>
var data = JSON.parse($('mydata').textContent)
[1] https://developer.mozilla.org/en-US/docs/HTML/Global_attribu...So just going <\/script> is enough.
<div id="data" class="{json: 'x'}"/>
you can mix actual classes with JSON, just put JSON at the end:
<div class="header inline {json: 'x'}"/>
CSS selectors will work just fine
JSON is fine with these characters, but JavaScript is not.
For plain-jane JSON this is usually fine, since you're not just evaluating the JSON as JavaScript, but are running the returned data through a JSON parser. A properly-designed JSON parser will escape any JSON-valid-but-JavaScript-invalid characters.
JSONP, however, works differently and will use use the JavaScript parser. Womp womp.
The blog post also lists two other cases, although the first case -- parsing JSON using eval -- is both insecure and incorrect. I haven't seen people do that in ages and ages.
https://github.com/jquery/jquery/blob/master/src/core.js#L52...
However, if you take a hex dump of the page, it becomes quite apparent:
https://gist.github.com/4393892
Note: The file is UTF-8 encoded, so you'd be looking for E2 80 A8 instead of \u2028.
[edit: i guess it's confused by the line break characters. i reported an issue.]
[edit2: whoa, wget seems to show the same thing when i look at the source in emacs...]
[edit3: ok, i am an idiot. it's just quotes in a bunch of meta tags. sorry. move along. nothing to see here.]
Sure, for NaN you can use null, but for Infinity, you have to use really large/small numbers, which can also lead to other problems.