Closed samuelcavendish closed 11 months ago
No, not a bug in Newtonsoft.Json.
Newtonsoft.Json is not changing the value here, and there are not really "decimal places added". You are just "victim" of the inherent imprecision of IEEE 754 floating point values. -0.4142917990684509 cannot be precisely stored as a IEEE 754 double
(64-bit) floating point value. The nearest possible value that can be represented by a double
is close to -0.414291799068450927734375
(it's still possibly not the exact base10 number, as the binary representation of a floating point number is not just a bit sequence of a single number but made of 3 distinct components - sign, base2 fraction and base2 exponent). In other words, item.Lng
does not - it cannot - really have a value of -0.4142917990684509.
This behavior is inherent to how the float
/double
types work in .NET and how the C# compiler processes floating point literals in source code, and you can't avoid this kind of imprecision unless you avoid using float
/double
(and instead use something like decimal
in your source data models you want to serialize, for example).
This being the result of the C# compiler trying to convert a value literal in the source code to a value that can actually be represented by a double
and not being something that is caused by Newtonsoft.Json can very easily be demonstrated by executing the following code:
double value = -0.4142917990684509;
Console.WriteLine(value.ToString("G17")); // output with 17 significant digits
Console.WriteLine(value.ToString("R")); // R format specifier is used by Newtonsoft.Json
The output of this will be, you guessed it, two times -0.41429179906845093
when this is compiled for a .NET Framework target. (Note that the behavior of the R
format specifiers differs between .NET Core 3.0 and newer versus .NET versions earlier than .NET Core 3.0.)
And as you already noticed, FloatParseHandling.Decimal
is a setting only affecting reading/parsing of Json text (e.g., deserialization), so there is no point nor effect in twiddling with the FloatParseHandling setting for serialization tasks...
If you don't mind the imprecision inherent to float
/double
and you only want to output floating point values with a specified maximum number of significant digits of your choice, create and use a JsonConverter that writes float
/double
in the desired way. If you target .NET versions older than .NET Core 3.0, you might want to write such a converter anyway, because Newtonsoft.Json itself uses the R
format specifier for writing floating point values, and R
is known to have problems regarding float/double-to-string conversion in .NET versions earlier than .NET Core 3.0. An illustrative example of such a converter for double
can be seen here: https://github.com/JamesNK/Newtonsoft.Json/issues/2889#issuecomment-1695255479.
Thanks for the response, I appreciate the extra info on core 3 and below
@elgonzo perfect answer as always 👍
When running the following Fiddle https://dotnetfiddle.net/U8DOMu you can see in the output that the Lng value has been changed and an extra decimal place has been added.
Looking at the code it seems the FloatParseHandling changes how values are read but isn't used for writing data
Fiddle code: