Skip to content

Commit d918bb8

Browse files
committed
CSHARP-1731: Use "G17" instead of "R" when converting Double to String for improved accuracy.
1 parent ed93b64 commit d918bb8

File tree

15 files changed

+106
-92
lines changed

15 files changed

+106
-92
lines changed

src/MongoDB.Bson/IO/JsonConvert.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ public static string ToString(Decimal128 value)
187187
/// <returns>A string.</returns>
188188
public static string ToString(double value)
189189
{
190-
return value.ToString("R", NumberFormatInfo.InvariantInfo);
190+
return value.ToString("G17", NumberFormatInfo.InvariantInfo);
191191
}
192192

193193
/// <summary>

src/MongoDB.Bson/IO/JsonWriter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ public override void WriteDouble(double value)
241241
}
242242

243243
// if string representation looks like an integer add ".0" so that it looks like a double
244-
var stringRepresentation = value.ToString("R", NumberFormatInfo.InvariantInfo);
244+
var stringRepresentation = JsonConvert.ToString(value);
245245
if (Regex.IsMatch(stringRepresentation, @"^[+-]?\d+$"))
246246
{
247247
stringRepresentation += ".0";

src/MongoDB.Bson/ObjectModel/Decimal128.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using System.Collections.Generic;
1818
using System.Globalization;
1919
using System.Text.RegularExpressions;
20+
using MongoDB.Bson.IO;
2021

2122
namespace MongoDB.Bson
2223
{
@@ -1414,7 +1415,7 @@ public Decimal128(decimal value)
14141415
public Decimal128(double value)
14151416
{
14161417
// TODO: implement this more efficiently
1417-
var stringValue = value.ToString("G17");
1418+
var stringValue = JsonConvert.ToString(value);
14181419
var decimal128Value = Decimal128.Parse(stringValue);
14191420
_highBits = MapIEEEHighBitsToDecimal128HighBits(decimal128Value.GetIEEEHighBits());
14201421
_lowBits = decimal128Value.GetIEEELowBits();
@@ -1428,7 +1429,7 @@ public Decimal128(double value)
14281429
public Decimal128(float value)
14291430
{
14301431
// TODO: implement this more efficiently
1431-
var stringValue = value.ToString("G17");
1432+
var stringValue = JsonConvert.ToString(value);
14321433
var decimal128Value = Decimal128.Parse(stringValue);
14331434
_highBits = MapIEEEHighBitsToDecimal128HighBits(decimal128Value.GetIEEEHighBits());
14341435
_lowBits = decimal128Value.GetIEEELowBits();

src/MongoDB.Bson/Serialization/Serializers/DoubleSerializer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2015 MongoDB Inc.
1+
/* Copyright 2010-2016 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -160,7 +160,7 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati
160160
break;
161161

162162
case BsonType.String:
163-
bsonWriter.WriteString(value.ToString("R", NumberFormatInfo.InvariantInfo));
163+
bsonWriter.WriteString(JsonConvert.ToString(value));
164164
break;
165165

166166
default:

src/MongoDB.Bson/Serialization/Serializers/SingleSerializer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2015 MongoDB Inc.
1+
/* Copyright 2010-2016 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -160,7 +160,7 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati
160160
break;
161161

162162
case BsonType.String:
163-
bsonWriter.WriteString(value.ToString("R", NumberFormatInfo.InvariantInfo));
163+
bsonWriter.WriteString(JsonConvert.ToString(value));
164164
break;
165165

166166
default:

tests/MongoDB.Bson.TestHelpers/XunitExtensions/RequireProcess.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public RequireProcess Bits(int bits)
3333
{
3434
return this;
3535
}
36-
throw new SkipTestException("Test skipped because process is a {actualBits}-bit process and not a {bits}-bit process.");
36+
throw new SkipTestException($"Test skipped because process is a {actualBits}-bit process and not a {bits}-bit process.");
3737
}
3838
}
3939
}

tests/MongoDB.Bson.Tests/IO/JsonWriterTests.cs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -178,25 +178,25 @@ public void TestDouble()
178178
var tests = new TestData<double>[]
179179
{
180180
new TestData<double>(0.0, "0.0"),
181-
new TestData<double>(0.0005, "0.0005"),
181+
new TestData<double>(0.0005, "0.00050000000000000001"),
182182
new TestData<double>(0.5, "0.5"),
183183
new TestData<double>(1.0, "1.0"),
184184
new TestData<double>(1.5, "1.5"),
185-
new TestData<double>(1.5E+40, "1.5E+40"),
186-
new TestData<double>(1.5E-40, "1.5E-40"),
185+
new TestData<double>(1.5E+40, "1.5000000000000001E+40"),
186+
new TestData<double>(1.5E-40, "1.5000000000000001E-40"),
187187
new TestData<double>(1234567890.1234568E+123, "1.2345678901234568E+132"),
188-
new TestData<double>(double.Epsilon, "4.94065645841247E-324"),
188+
new TestData<double>(double.Epsilon, "4.9406564584124654E-324"),
189189
new TestData<double>(double.MaxValue, "1.7976931348623157E+308"),
190190
new TestData<double>(double.MinValue, "-1.7976931348623157E+308"),
191191

192-
new TestData<double>(-0.0005, "-0.0005"),
192+
new TestData<double>(-0.0005, "-0.00050000000000000001"),
193193
new TestData<double>(-0.5, "-0.5"),
194194
new TestData<double>(-1.0, "-1.0"),
195195
new TestData<double>(-1.5, "-1.5"),
196-
new TestData<double>(-1.5E+40, "-1.5E+40"),
197-
new TestData<double>(-1.5E-40, "-1.5E-40"),
196+
new TestData<double>(-1.5E+40, "-1.5000000000000001E+40"),
197+
new TestData<double>(-1.5E-40, "-1.5000000000000001E-40"),
198198
new TestData<double>(-1234567890.1234568E+123, "-1.2345678901234568E+132"),
199-
new TestData<double>(-double.Epsilon, "-4.94065645841247E-324"),
199+
new TestData<double>(-double.Epsilon, "-4.9406564584124654E-324"),
200200

201201
new TestData<double>(double.NaN, "NaN"),
202202
new TestData<double>(double.NegativeInfinity, "-Infinity"),
@@ -210,6 +210,18 @@ public void TestDouble()
210210
}
211211
}
212212

213+
[SkippableFact]
214+
public void TestDoubleRoundTripOn64BitProcess()
215+
{
216+
RequireProcess.Check().Bits(64);
217+
var value = 0.6822871999174; // see: https://msdn.microsoft.com/en-us/library/dwhawy9k(v=vs.110).aspx#RFormatString
218+
219+
var json = value.ToJson();
220+
var rehydrated = BsonSerializer.Deserialize<double>(json);
221+
222+
rehydrated.Should().Be(value);
223+
}
224+
213225
[Fact]
214226
public void TestInt64Shell()
215227
{

tests/MongoDB.Bson.Tests/Serialization/Serializers/TimeSpanSerializerTests.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2014 MongoDB Inc.
1+
/* Copyright 2010-2016 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
1717
using System.Globalization;
1818
using System.Text.RegularExpressions;
1919
using MongoDB.Bson;
20+
using MongoDB.Bson.IO;
2021
using MongoDB.Bson.Serialization;
2122
using MongoDB.Bson.Serialization.Attributes;
2223
using MongoDB.Bson.Serialization.Options;
@@ -34,7 +35,7 @@ public interface IC
3435
{
3536
public static void TestValue(long ticks, double value)
3637
{
37-
var jsonValue = value.ToString("R", NumberFormatInfo.InvariantInfo);
38+
var jsonValue = JsonConvert.ToString(value);
3839
if (Regex.IsMatch(jsonValue, @"^-?\d+$")) { jsonValue += ".0"; } // if string looks like an integer add ".0" to match JsonWriter format
3940
TestValue(ticks, jsonValue);
4041
}
@@ -203,10 +204,10 @@ public class C : IC
203204
[InlineData(0.0, "0.0")]
204205
[InlineData(100.0, "100.0")] // 1 Tick
205206
[InlineData(1100.0, "1100.0")] // only multiples of 100 can be round tripped
206-
[InlineData(92233720368547700.0, "9.22337203685477E+16")] // almost Int64.MaxValue
207+
[InlineData(92233720368547700.0, "92233720368547696.0")] // almost Int64.MaxValue
207208
[InlineData(-100.0, "-100.0")]
208209
[InlineData(-1100.0, "-1100.0")]
209-
[InlineData(-92233720368547700.0, "-9.22337203685477E+16")] // almost Int64.MinValue
210+
[InlineData(-92233720368547700.0, "-92233720368547696.0")] // almost Int64.MinValue
210211
public void TestNanoseconds(double nanoseconds, string jsonValue)
211212
{
212213
var ticks = (long)(nanoseconds / __nanosecondsPerTick);

tests/MongoDB.Driver.Legacy.Tests/Builders/IndexOptionsBuilderTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2015 MongoDB Inc.
1+
/* Copyright 2010-2016 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -49,8 +49,8 @@ public void TestDropDups()
4949
[Fact]
5050
public void TestGeoSpatialRange()
5151
{
52-
var options = IndexOptions.SetGeoSpatialRange(1.1, 2.2);
53-
string expected = "{ \"min\" : 1.1, \"max\" : 2.2 }";
52+
var options = IndexOptions.SetGeoSpatialRange(1.5, 2.5);
53+
string expected = "{ \"min\" : 1.5, \"max\" : 2.5 }";
5454
Assert.Equal(expected, options.ToJson());
5555
}
5656

@@ -113,8 +113,8 @@ public void TestNameDropDups()
113113
[Fact]
114114
public void TestNameGeoSpatialRange()
115115
{
116-
var options = IndexOptions.SetName("custom").SetGeoSpatialRange(1.1, 2.2);
117-
string expected = "{ \"name\" : \"custom\", \"min\" : 1.1, \"max\" : 2.2 }";
116+
var options = IndexOptions.SetName("custom").SetGeoSpatialRange(1.5, 2.5);
117+
string expected = "{ \"name\" : \"custom\", \"min\" : 1.5, \"max\" : 2.5 }";
118118
Assert.Equal(expected, options.ToJson());
119119
}
120120

tests/MongoDB.Driver.Legacy.Tests/Builders/IndexOptionsBuilderTypedTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2015 MongoDB Inc.
1+
/* Copyright 2010-2016 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -57,8 +57,8 @@ public void TestDropDups()
5757
[Fact]
5858
public void TestGeoSpatialRange()
5959
{
60-
var options = IndexOptions<TestClass>.SetGeoSpatialRange(1.1, 2.2);
61-
string expected = "{ \"min\" : 1.1, \"max\" : 2.2 }";
60+
var options = IndexOptions<TestClass>.SetGeoSpatialRange(1.5, 2.5);
61+
string expected = "{ \"min\" : 1.5, \"max\" : 2.5 }";
6262
Assert.Equal(expected, options.ToJson());
6363
}
6464

@@ -121,8 +121,8 @@ public void TestNameDropDups()
121121
[Fact]
122122
public void TestNameGeoSpatialRange()
123123
{
124-
var options = IndexOptions<TestClass>.SetName("custom").SetGeoSpatialRange(1.1, 2.2);
125-
string expected = "{ \"name\" : \"custom\", \"min\" : 1.1, \"max\" : 2.2 }";
124+
var options = IndexOptions<TestClass>.SetName("custom").SetGeoSpatialRange(1.5, 2.5);
125+
string expected = "{ \"name\" : \"custom\", \"min\" : 1.5, \"max\" : 2.5 }";
126126
Assert.Equal(expected, options.ToJson());
127127
}
128128

0 commit comments

Comments
 (0)