Double to string conversion problem
Target name(s)
ALL
Firmware version
newest
Was working before? On which version?
I think it worked better a year ago
Device capabilities
No response
Description
Converting double to string does not work properly. For example, 14 is converted to 13.999999999999999, 92 to 91.999999999999993.
I am aware of how floating point numbers work and that some cannot be written without error, but the numbers 14 or 92 are not like that.
How to reproduce
Run this program:
using System;
namespace DoubleToStringTest
{
public class Program
{
public static void Main()
{
for (int i = 0; i < 100; i++)
{
double d = i;
Console.WriteLine($"{i} {d}");
}
}
}
}
Program prints:
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
11 11
12 12
13 13
14 13.999999999999999
15 15
16 16
17 17
18 18
19 19
20 20
21 21
22 22
23 22.999999999999998
24 23.999999999999999
25 25
26 26
27 27
28 27.999999999999998
29 29
30 30
31 31
32 32
33 32.999999999999998
34 33.999999999999999
35 35
36 36
37 37
38 37.999999999999998
39 39
40 40
41 40.999999999999996
42 42
43 42.999999999999998
44 44
45 45
46 45.999999999999996
47 47
48 47.999999999999998
49 49
50 50
51 50.999999999999996
52 52
53 52.999999999999998
54 54
55 55
56 55.999999999999996
57 57
58 57.999999999999998
59 59
60 60
61 60.999999999999996
62 62
63 62.999999999999998
64 64
65 65
66 65.999999999999996
67 67
68 67.999999999999998
69 69
70 70
71 70.999999999999996
72 72
73 72.999999999999998
74 74
75 75
76 75.999999999999996
77 77
78 77.999999999999998
79 79
80 80
81 80.999999999999996
82 81.999999999999993
83 83
84 84
85 85
86 85.999999999999996
87 86.999999999999993
88 88
89 89
90 90
91 90.999999999999996
92 91.999999999999993
93 93
94 94
95 95
96 95.999999999999996
97 96.999999999999993
98 98
99 99
Expected behaviour
Program should print:
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
11 11
12 12
13 13
14 14
15 15
16 16
17 17
18 18
19 19
20 20
21 21
22 22
23 23
24 24
25 25
26 26
27 27
28 28
29 29
30 30
31 31
32 32
33 33
34 34
35 35
36 36
37 37
38 38
39 39
40 40
41 41
42 42
43 43
44 44
45 45
46 46
47 47
48 48
49 49
50 50
51 51
52 52
53 53
54 54
55 55
56 56
57 57
58 58
59 59
60 60
61 61
62 62
63 63
64 64
65 65
66 66
67 67
68 68
69 69
70 70
71 71
72 72
73 73
74 74
75 75
76 76
77 77
78 78
79 79
80 80
81 81
82 82
83 83
84 84
85 85
86 86
87 87
88 88
89 89
90 90
91 91
92 92
93 93
94 94
95 95
96 96
97 97
98 98
99 99
Screenshots
No response
Aditional information
No response
This issue might be the similar to https://github.com/nanoframework/Home/issues/1401 where the double is being cast to a float and that causes a loss of precision.
Duplicate with #1429 See that specific issue for the answer. I will close this issue.
@Ellerbach I must disagree that this is not a bug. While it is understood that floating-point arithmetic can introduce small errors due to the way numbers are represented in binary, the numbers 14 and 92 can be exactly represented in the IEEE 754 standard used for floating-point arithmetic. Therefore, when converted to a string, they should retain their exact integer representation without any introduced imprecision.
Even (as you wrote) less smart languages like Python can deal with the numbers 14 or 92 but nanoFramework not.
Another thing that makes something wrong is that this code
double d = 14d;
Console.WriteLine($"{d}");
will print 13.999999999999999 and the use of float, a less precise variable,
float f = 14f;
Console.WriteLine($"{f}");
already works properly
Additionally, I looked around the code and there is even a test that checks this https://github.com/nanoframework/CoreLibrary/blob/main/Tests/NFUnitTestArithmetic/UnitTestFormat.cs#L189 but the selected number in the test does not cause a problem. Replacing -1234 with -14 would cause this test to fail.
OK, reopening the issue. That say, it depends of the device precision. Double is always available, but if not natively supported, will automatically fall back to float.
Actually, that's the other way around: float is always available. Double is natively supported if the platform has DP hardware support (or emulated). Rational for this is explained in our docs here
Getting back to this matter.
If the sample code provided above is changed to float d the casts and output work as expected.
The reason for this is explained on our Architecture docs about Floating-point calculations.
The default build option for the precompiled firmware images we provide has DP (double precision) calculation turned OFF, thus the "backing type" for these calculations is float type.
I'll copy here an excerpt of the referenced doc "(...) the extra precision provided by the double type is seldom required on typical embedded application use cases."
Offering a precision of ~6-9 digits, with a size of 4 bytes and saving precious flash space is considered (by design) more than adequate for the vast majority of embedded world applications. This behavior is by design and well documented.
Adding to this, the fact that the build option DP_FLOATINGPOINT is provided, allowing builds with support for double precision FP which will enable the use of double type without any loss of precision, gives developers the choice of using it, if their application calls for such precision.
Considering all the above, I'm closing the issue. Feel free to continue the conversation if more clarification is required.
@MateuszKlatecki to let you know that during testing for nanoframework/nf-interpreter#3020 it was found that the issue reported here wasn't caused by the value carried by the variable, rather by the code that was composing the string output corresponding to the value.
Long story short: the value was perfectly fine. The output of ToString() was the culprit.
This is now fixed and the output shows correct values for all those integers.