D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 15885 - float serialized to JSON loses precision
Summary: float serialized to JSON loses precision
Status: RESOLVED FIXED
Alias: None
Product: D
Classification: Unclassified
Component: phobos (show other issues)
Version: D2
Hardware: All All
: P1 major
Assignee: basile-z
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2016-04-06 12:29 UTC by Rory
Modified: 2020-03-21 03:56 UTC (History)
1 user (show)

See Also:


Attachments
possible patch (460 bytes, patch)
2016-04-06 13:42 UTC, Rory
Details | Diff

Note You need to log in before you can comment on or make changes to this issue.
Description Rory 2016-04-06 12:29:56 UTC
I've marked this issue as major because a lot of people are using JSON for mobile app communication, and I discovered this because an incorrect invoice was sent (fortunately only during testing).

In this code:
import std.json;
import std.stdio;

void main() {
	JSONValue json;
	json.type = JSON_TYPE.FLOAT;
	json.floating = 30738.22;
	writeln(toJSON(&json));
}

the resulting output is:
30738.2


The problem is that all float serialization just uses to!string() from std.conv;
std.json should serialize to the standard @ json.org.
Comment 1 Rory 2016-04-06 13:42:13 UTC
Created attachment 1594 [details]
possible patch

this patch just makes sure we always output using %f. I'm guessing %g technically has the bug because surely it shouldn't just remove part of the number _by default_
Comment 2 basile-z 2016-05-21 20:40:56 UTC
(In reply to Rory from comment #1)
> Created attachment 1594 [details]
> possible patch
> 
> this patch just makes sure we always output using %f. I'm guessing %g
> technically has the bug because surely it shouldn't just remove part of the
> number _by default_

%f doesn't work. It breaks some unittest.

the correct format specifier would be "%.9g" (9 comes from the constant FLT_DECIMAL_DIG).

see https://github.com/dlang/phobos/pull/4343
Comment 3 thomas.bockman 2016-05-22 02:37:51 UTC
The correct format specifier is ACTUALLY "%.18g", assuming that you want full lossless conversion. This is because:

1) JSON_TYPE.FLOAT is a 64-bit double, not 32-bit float!

2) cast(int)ceil(log(pow(2.0L, F.mant_dig - 1)) / log(10.0L) + 1) decimal digits are needed to represent some floating-point values, such as (1 + F.epsilon).

See this DPaste: https://dpaste.dzfl.pl/1bd14e5c3f83#line-25
Comment 4 github-bugzilla 2016-06-01 05:31:16 UTC
Commits pushed to master at https://github.com/dlang/phobos

https://github.com/dlang/phobos/commit/7a486d9d038448595c74aa4ef4bd7d9e952a4b64
Fix issue 15885 - numeric values serialized to JSON lose precision.

https://github.com/dlang/phobos/commit/f4ad734aad6e3b2dd4881508d2b15eebb732a26c
Merge pull request #4345 from tsbockman/issue-15885-tsb

Fix issue 15885 - float serialized to JSON loses precision
Comment 5 github-bugzilla 2016-10-01 11:44:57 UTC
Commits pushed to stable at https://github.com/dlang/phobos

https://github.com/dlang/phobos/commit/7a486d9d038448595c74aa4ef4bd7d9e952a4b64
Fix issue 15885 - numeric values serialized to JSON lose precision.

https://github.com/dlang/phobos/commit/f4ad734aad6e3b2dd4881508d2b15eebb732a26c
Merge pull request #4345 from tsbockman/issue-15885-tsb