It looks like this should work. According to my tests, it also round-trips special values like infinity and NaN just fine.
static string SerializeDouble(double d)
{
return d.ToString("G17", CultureInfo.InvariantCulture);
}
static double DeserializeDouble(string s)
{
return double.Parse(s, CultureInfo.InvariantCulture);
}
Why "G17"
?
Source: https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings?redirectedfrom=MSDN#GFormatString
Relevant quote (at the time of this post):
Note that, when used with a Double value, the "G17" format specifier ensures that the original Double value successfully round-trips. This is because Double is an IEEE 754-2008-compliant double-precision (binary64) floating point number that gives up to 17 significant digits of precision. We recommend its use instead of the "R" format specifier, since in some cases "R" fails to successfully round-trip double-precision floating point values.
Why CultureInfo.InvariantCulture
?
This takes care of any culture-specific formatting that would otherwise break your round-trip. For example, this code snippet gives some very undesirable results!
double d0 = Math.PI;
Console.WriteLine("d0: " + d0); // prints 3.14159265358979
// Serialize in America
string s = d0.ToString("G17", new CultureInfo("en-us"));
Console.WriteLine("s: " + s); // prints 3.1415926535897931
// Deserialize in Germany
double d1 = double.Parse(s, new CultureInfo("de-de"));
Console.WriteLine("d1: " + d1); // prints 3.14159265358979E+16