Float rounding (in JSON)
Steven Schveighoffer
schveiguy at gmail.com
Thu Oct 13 19:27:22 UTC 2022
On 10/13/22 3:00 PM, Sergey wrote:
> I'm not a professional of IEEE 754, but just found this behavior at
> rounding in comparison with other languages. I supose it happened
> because in D float numbers parsed as double and have a full length of
> double while rounding. But this is just doesn't match with behavior in
> other languages.
>
> I'm not sure if this is somehow connected with JSON realizations.
It doesn't look really that far off. You can't expect floating point
parsing to be exact, as floating point does not perfectly represent
decimal numbers, especially when you get down to the least significant bits.
>
> Is it possible in D to have the same results as in others? Because
> explicit formatting is not the answer, since length of rounding could be
> different. That's why just specify "%.XXf" will not resolve the issue -
> two last numbers have 14 and 15 positions after the dot.
It seems like you are looking to output a certain number of digits. If
you limit the digits, you can get the outcome you desire.
But I want to point out something you may have missed:
>
> **Code Python**
> ```python
> import json
>
> str = '{ "f1": 43.476379000000065, "f2": 43.499718999999987, "f3":
> 43.499718000000087, "f4": 43.418052999999986 }'
> print(json.loads(str))
> ```
> **Result**
> ```
> {'f1': 43.476379000000065, 'f2': 43.499718999999985, 'f3':
> 43.49971800000009, 'f4': 43.418052999999986}
> ```
Let's line these up so we can read it easier
```
f1 in: 43.476379000000065
f1 out: 43.476379000000065
f2 in: 43.499718999999987
f2 out: 43.499718999999985
f3 in: 43.499718000000087
f3 out: 43.49971800000009
f4 in: 43.418052999999986
f4 out: 43.418052999999986
```
Note how f2 is a different output significantly than the input. This is
an artifact of floating point parsing and the digits that are the most
insignificant.
Also note that the omission of the 7 in f3 doesn't seem to have to do
with rounding, because the digits are less than the original. If that
digit were anywhere close to significant, you would have expected the
digit to appear.
>
> **Code Crystal**
> ```crystal
> require "json"
>
> str = "{ \"f1\": 43.476379000000065, \"f2\": 43.499718999999987, \"f3\":
> 43.499718000000087, \"f4\": 43.418052999999986 }"
>
> puts JSON.parse(str)
> ```
> **Result**
> ```
> {"f1" => 43.476379000000065, "f2" => 43.499718999999985, "f3" =>
> 43.49971800000009, "f4" => 43.418052999999986}
> ```
Same here
>
> **Code D**
> ```d
> import std;
>
> void main() {
> string s = `{ "f1": 43.476379000000065, "f2": 43.499718999999987,
> "f3": 43.499718000000087, "f4": 43.418052999999986 }`;
> JSONValue j = parseJSON(s);
> writeln(j);
> }
> ```
> **Result**
> ```
> {"f1":43.4763790000000654,"f2":43.4997189999999847,"f3":43.4997180000000867,"f4":43.4180529999999862}
> ```
Let's look at D's representation:
```
f1 in: 43.476379000000065
f1 out: 43.4763790000000654
f2 in: 43.499718999999987
f2 out: 43.4997189999999847
f3 in: 43.499718000000087
f3 out: 43.4997180000000867
f4 in: 43.418052999999986
f4 out: 43.4180529999999862
```
Why does it print one more digit than the other languages? Because that
must be the default for `writeln`. You can affect this by changing the
number of digits printed. But probably not when printing an entire JSON
structure.
But look also at f3, and how actually D is closer to the expected value
than with the other languages.
If you want exact representation of data, parse it as a string instead
of a double.
I'm assuming you are comparing for testing purposes? If you are, just
realize you can never be accurate here. You just have to live with the
difference. Typically when comparing floating point values, you use an
epsilon to ensure that the floating point value is "close enough", you
can't enforce exact representation.
-Steve
More information about the Digitalmars-d-learn
mailing list