Hi Walter,
Apologies for the delay. I drafted a response here and sat on it longer
than I meant to. You'll see why: it is far out of bounds for a mailing
list about Jansson, and I'm out of my domain here.
Please be cautious. Waltzing through a minefield is a bad idea, even if
someone has told you it's probably do-able.
Comments inline.
On 2020-09-08 12:51 p.m., Walter Both wrote:
> Thanks Graeme and Petri for your quick answers.
>
> As you guessed correctly Graeme, I am looking primarily at having more
> control over double precision numbers with all their unexpected
> behaviour. Our backend application is written in C. So we now all too
> well what practical implications can show up if you use double precision
> numbers for storing prices. For that reason we use the common solution
> for prices in our backend; we store them in integers and seperatly we
> store how many decimals they contain. Of course we could create a
> similar presentation in JSON. But it requires extra documentation and
> explanation to develpors connecting to our API. Send the prices as
> floats will be more self explainatory. But as said with double precision
> numbers we would need more control over the number of decimals used in
> the 'printf' call in the function 'json_dumps'. That would (for a large
> part?) hide the rounding errors. Otherwise we probaly have to define the
> prices as a string but that looks less attractive and elegant. On the
> other hand you are right to mention the fact that the receiving and will
> still have the problem not being able to store that double precision
> value in a double.
Here's as concise a statement of the problem as I can make: consider a
fraction like 0.10. As a double, this has the approximate encoding
0x3fb999999999999a
The repeating binary digit is visible in the trailing .....999999a's: if
you replaced the double with a longer floating-point format it would
keep trailing 9's forever. Depending on your fraction, the repeating
pattern may be longer or shorter (or your encoding may be exact, like 0.25.)
You are trying to avoid any user-visible impacts from from this
inaccuracy in your JSON. Your back-end implementation uses safer design
practices, so this may be the first time your currencies are coaxed into
a floating-point number at all.
Even if I can't precisely encode decimals like 0.10 in floating point, I
(and most double-backed JSON implementations) will serialize the numbers
the way you want anyhow. Jansson and Python do the same thing here:
>>> import json
>>> json.dumps(0.10)
'0.1'
You just have to be aware about nearby encodings:
>>> import json
>>> import np
>>> json.dumps(0.01 + 0.09)
'0.09999999999999999' # ....oh no
>>> json.dumps(np.round(0.01 + 0.09, 2))
'0.1' # ...ok
This works for dimes (0.10) and pennies (0.01). Other fractions have
different approximations, so e.g. internationalization adds its own
problems.
Increasing the absolute size of your price also eventually wrecks the
encoding ($1000000000000000000.01 is problematic) because floating-point
has finite dynamic range. If your quantities are bounded this may be
avoidable.
Finally, you are relying on more than just Jansson for consistency here:
your float-to-string conversion is ultimately part of libc, so you're
exposing yourself to an unexpected impact from a deeper part of the
runtime than seems logical at first.
But: you may be able to coax Jansson into serializing quantities
appropriately without deep changes to Jansson itself, just by
understanding what you're doing and doing it carefully. You'll need to
pre-round your prices (similar to the last Python example) so trailing
digits don't creep in. You will want to be very deliberate about your
regression testing so that the assumptions you're making during design
are not violated later on.
Thoughts?
> We are a group of a couple of c-coders with quite some years of
> experience, so we could make the changes. The primary aim of the
> question is to inquire if this change would break the specs to which
> JSON should conform and if we would push the changes, whether they might
> be consired for acceptance or if they would be rejected by definition
> straight away.
We're now switching gears to consider a contribution to Jansson. I
recommend you start with the "bignum" bug report here:
https://github.com/akheron/jansson/issues/69
This is a very closely related stable of problems to yours. The fact
that this bug report is open (and was opened by Jansson's primary
develper) indicates that we're eager for a well-crafted bignum patch.
OTOH it's still open, hinting it's non-trivial and that some very strong
coders have not been successful yet.
It is possible (but not guaranteed) that a bignum patch would give you
the hooks you need. It's also possible this is the wrong approach for
you, but that these materials are a good starting point anyways.
> P.S.
> Do you have any insight in what approach people often take when they
> need to put double precision values in JSON with the problems as
> mentioned in the url
https://floating-point-gui.de/basic/
Facetiously: denial, anger, bargaining, depression, and acceptance. I
hope the approach I first sketched above falls into the "acceptance"
category. There is certainly nothing tidy about pushing your currencies
into strings. Strings are more future-proof for internationalization. On
the other hand, strings are a wholesale dodge around serialization and
do not offer the most friendly or logical API.
best,
Graeme
> <mailto:
jansson-user...@googlegroups.com>.
> To view this discussion on the web visit
>
https://groups.google.com/d/msgid/jansson-users/3fd3b7b0-fb81-4122-9b17-28c83f70a029n%40googlegroups.com
> <
https://groups.google.com/d/msgid/jansson-users/3fd3b7b0-fb81-4122-9b17-28c83f70a029n%40googlegroups.com?utm_medium=email&utm_source=footer>.