Multiplication error on double variable [duplicate] - c#

I have this test code:
class Test
{
static void Main()
{
decimal m = 1M / 6M;
double d = 1.0 / 6.0;
decimal notQuiteWholeM = m + m + m + m + m + m; // 1.0000000000000000000000000002M
double notQuiteWholeD = d + d + d + d + d + d; // 0.99999999999999989
Console.WriteLine(notQuiteWholeM); // Prints: 1.0000000000000000000000000002
Console.WriteLine(notQuiteWholeD); // Prints: 1.
Console.WriteLine(notQuiteWholeM == 1M); // False
Console.WriteLine(notQuiteWholeD < 1.0); // Prints: True. Why?
Console.ReadKey();
}
}
Why this line prints 1?
Console.WriteLine(notQuiteWholeD); // Prints: 1
an this one, why prints True?
Is there an automatically rounding process? What can I do to print the correct/calculated value?
[Note: I found this example code in C# 5.0 in a Nutsheel page 30: Real Number Rounding Error].
Thanks in advance.

Not quite reading your question in the same way as the other two answers. The gist of it: Does the formatted string representation of a double "round" in C#?
Yes.
Internally double is represented with full IEEE-754 decimal digit precision (15-17 digits), which is why:
notQuiteWholeD < 1.0 == true // because notQuiteWholeD = 0.99999999999999989
However, when formatting it as a string, by default it will use 15 digit precision - equivalent to:
String.Format("{0:G15}", notQuiteWholeD) // outputs "1"
To get all the digits of the full internal representation, you can use:
Console.WriteLine("{0:G17}", notQuiteWholeD);
Or:
Console.WriteLine("{0:R}", notQuiteWholeD);
Both, in this case, will output "0,99999999999999989".
The former will always use 17 digit precision. The latter ("roundtrip precision") will use 15 digits if that's enough precision for the following to be true, otherwise it will use 17:
Double.Parse(String.Format("{0:G15}", notQuiteWholeD)) == notQuiteWholeD
Bonus Example:
... of when G17 and R differ:
Console.WriteLine("{0:G17}", 1.0000000000000699); // outputs "1.0000000000000699"
Console.WriteLine("{0:R}", 1.0000000000000699); // outputs "1.00000000000007"
1.0000000000000699 (17 significant digits) can be represented accurately enough for a roundtrip using only 15 significant digits. In other words, the double representation of 1.00...07 is the same as for 1.00...0699.
So 1.00...07 (15 digits) is a shorter input to get the exact same internal (17 digit) representation. That means R will round it to 15 digits, while G17 will keep all the digits of the internal representation.
Maybe it's clearer when realizing that this:
Console.WriteLine("{0:G17}", 1.00000000000007); // outputs "1.0000000000000699"
Console.WriteLine("{0:R}", 1.00000000000007); // outputs "1.00000000000007"
... gives the exact same results.

Decimal is stored in terms of base 10. Double is stored in terms of base 2. Neither of those bases can exactly represent 1 / 6 with a finite representation.
That explains all the output except Console.WriteLine(notQuiteWholeD). You get "1" for output, even though the actual value stored is less than 1. Since the output is in base 10, it has to convert from base 2. Part of the conversion includes rounding.

As we know, 1/6 = 0.1666 (repeating), decimal and double can not represent repeating numbers, they are calculated when assigned to. Since they are built from different backing data structures they represent a different set of possible numbers and round differently in some cases.
For this code:
Console.WriteLine(notQuiteWholeD < 1.0); // Prints: True. Why?
Since notQuiteWholeD is 0.99999999999999989 it prints true.
I'm not going to cover how the double and decimal work behind the scenes but here is some reading material if you're interested.
Double-precision floating-point format
SO - Behind the scenes, what's happening with decimal value type in C#/.NET?
Decimal floating point in .NET

Related

Exponents in C#

int trail = 14;
double mean = 14.00000587000000;
double sd = 4.47307944700000;
double zscore = double.MinValue;
zscore = (trail - mean) / sd; //zscore at this point is exponent value -1.3122950464645662E-06
zscore = Math.Round(zscore, 14); //-1.31229505E-06
Math.Round() also keeps the exponent value. should zscore.ToString("F14") be used instead of Math.Round() function to convert it to non-exponent value? Please explain.
These are completely independant concerns.
Math.Round will actually return a new value, rounded to the specified decimal (ar at least, as near as one can do with floating point).
You can reuse this result value anywhere, and show it with 16 decimals precision if you want, but it's not supposed to be the same as the original one.
The fact that it is displayed with exponent notation or not has nothing to do with Round.
When you use ToString("F14") on a number, this is a display specification only, and does not modify the underlying value in any way. The underlying value might be a number that would or would not display as exponential notation otherwise, and may or may actually have 14 significant digits.
It simply forces the number to be displayed as a full decimal without exponent notation, with the number of digits specified. So it seems to be what you actually want.
Examples :
(executable online here : http://rextester.com/PZXDES55622)
double num = 0.00000123456789;
Console.WriteLine("original :");
Console.WriteLine(num.ToString());
Console.WriteLine(num.ToString("F6"));
Console.WriteLine(num.ToString("F10"));
Console.WriteLine(num.ToString("F14"));
Console.WriteLine("rounded to 6");
double rounded6 = Math.Round(num, 6);
Console.WriteLine(rounded6.ToString());
Console.WriteLine(rounded6.ToString("F6"));
Console.WriteLine(rounded6.ToString("F10"));
Console.WriteLine(rounded6.ToString("F14"));
Console.WriteLine("rounded to 10");
double rounded10 = Math.Round(num, 10);
Console.WriteLine(rounded10.ToString());
Console.WriteLine(rounded10.ToString("F6"));
Console.WriteLine(rounded10.ToString("F10"));
Console.WriteLine(rounded10.ToString("F14"));
will output:
original :
1,23456789E-06
0,000001
0,0000012346
0,00000123456789
rounded to 6
1E-06
0,000001
0,0000010000
0,00000100000000
rounded to 10
1,2346E-06
0,000001
0,0000012346
0,00000123460000

Double to Decimal without rounding after 15 digits

When converting a "high" precision Double to a Decimal I lose precision with Convert.ToDecimal or casting to (Decimal) due to Rounding.
Example :
double d = -0.99999999999999956d;
decimal result = Convert.ToDecimal(d); // Result = -1
decimal result = (Decimal)(d); // Result = -1
The Decimal value returned by Convert.ToDecimal(double) contains a maximum of 15 significant digits. If the value parameter contains more than 15 significant digits, it is rounded using rounding to nearest.
So I in order to keep my precision, I have to convert my double to a String and then call Convert.ToDecimal(String):
decimal result = System.Convert.ToDecimal(d.ToString("G20")); // Result = -0.99999999999999956d
This method is working but I would like to avoid using a String variable in order to convert a Double to Decimal without rounding after 15 digits?
One possible solution is to decompose d as the exact sum of n doubles, the last of which is small and contains all the trailing significant digits that you desire when converted to decimal, and the first (n-1) of which convert exactly to decimal.
For the source double d between -1.0 and 1.0:
decimal t = 0M;
bool b = d < 0;
if (b) d = -d;
if (d >= 0.5) { d -= 0.5; t = 0.5M; }
if (d >= 0.25) { d -= 0.25; t += 0.25M; }
if (d >= 0.125) { d -= 0.125; t += 0.125M; }
if (d >= 0.0625) { d -= 0.0625; t += 0.0625M; }
t += Convert.ToDecimal(d);
if (b) t = -t;
Test it on ideone.com.
Note that the operations d -= are exact, even if C# computes the binary floating-point operations at a higher precision than double (which it allows itself to do).
This is cheaper than a conversion from double to string, and provides a few additional digits of accuracy in the result (four bits of accuracy for the above four if-then-elses).
Remark: if C# did not allow itself to do floating-point computations at a higher precision, a good trick would have been to use Dekker splitting to split d into two values d1 and d2 that would convert each exactly to decimal. Alas, Dekker splitting only works with a strict interpretation of IEEE 754 multiplication and addition.
Another idea is to use C#'s version of frexp to obtain the significand s and exponent e of d, and to compute (Decimal)((long) (s * 4503599627370496.0d)) * <however one computes 2^e in Decimal>.
There are two approaches, one of which will work for values up below 2^63, and the other of which will work for values larger than 2^53.
Split smaller values into whole-number and fractional parts. The whole-number part may be precisely cast to long and then Decimal [note that a direct cast to Decimal may not be precise!] The fractional part may be precisely multiplied by 9007199254740992.0 (2^53), converted to long and then Decimal, and then divided by 9007199254740992.0m. Adding the result of that division to the whole-number part should yield a Decimal value which is within one least-significant-digit of being correct [it may not be precisely rounded, but will still be far better than the built-in conversions!]
For larger values, multiply by (1.0/281474976710656.0) (2^-48), take the whole-number part of that result, multiply it back by 281474976710656.0, and subtract it from the original result. Convert the whole-number results from the division and the subtraction to Decimal (they should convert precisely), multiply the former by 281474976710656m, and add the latter.

Inconsistency rounding real numbers in C #

I have this test code:
class Test
{
static void Main()
{
decimal m = 1M / 6M;
double d = 1.0 / 6.0;
decimal notQuiteWholeM = m + m + m + m + m + m; // 1.0000000000000000000000000002M
double notQuiteWholeD = d + d + d + d + d + d; // 0.99999999999999989
Console.WriteLine(notQuiteWholeM); // Prints: 1.0000000000000000000000000002
Console.WriteLine(notQuiteWholeD); // Prints: 1.
Console.WriteLine(notQuiteWholeM == 1M); // False
Console.WriteLine(notQuiteWholeD < 1.0); // Prints: True. Why?
Console.ReadKey();
}
}
Why this line prints 1?
Console.WriteLine(notQuiteWholeD); // Prints: 1
an this one, why prints True?
Is there an automatically rounding process? What can I do to print the correct/calculated value?
[Note: I found this example code in C# 5.0 in a Nutsheel page 30: Real Number Rounding Error].
Thanks in advance.
Not quite reading your question in the same way as the other two answers. The gist of it: Does the formatted string representation of a double "round" in C#?
Yes.
Internally double is represented with full IEEE-754 decimal digit precision (15-17 digits), which is why:
notQuiteWholeD < 1.0 == true // because notQuiteWholeD = 0.99999999999999989
However, when formatting it as a string, by default it will use 15 digit precision - equivalent to:
String.Format("{0:G15}", notQuiteWholeD) // outputs "1"
To get all the digits of the full internal representation, you can use:
Console.WriteLine("{0:G17}", notQuiteWholeD);
Or:
Console.WriteLine("{0:R}", notQuiteWholeD);
Both, in this case, will output "0,99999999999999989".
The former will always use 17 digit precision. The latter ("roundtrip precision") will use 15 digits if that's enough precision for the following to be true, otherwise it will use 17:
Double.Parse(String.Format("{0:G15}", notQuiteWholeD)) == notQuiteWholeD
Bonus Example:
... of when G17 and R differ:
Console.WriteLine("{0:G17}", 1.0000000000000699); // outputs "1.0000000000000699"
Console.WriteLine("{0:R}", 1.0000000000000699); // outputs "1.00000000000007"
1.0000000000000699 (17 significant digits) can be represented accurately enough for a roundtrip using only 15 significant digits. In other words, the double representation of 1.00...07 is the same as for 1.00...0699.
So 1.00...07 (15 digits) is a shorter input to get the exact same internal (17 digit) representation. That means R will round it to 15 digits, while G17 will keep all the digits of the internal representation.
Maybe it's clearer when realizing that this:
Console.WriteLine("{0:G17}", 1.00000000000007); // outputs "1.0000000000000699"
Console.WriteLine("{0:R}", 1.00000000000007); // outputs "1.00000000000007"
... gives the exact same results.
Decimal is stored in terms of base 10. Double is stored in terms of base 2. Neither of those bases can exactly represent 1 / 6 with a finite representation.
That explains all the output except Console.WriteLine(notQuiteWholeD). You get "1" for output, even though the actual value stored is less than 1. Since the output is in base 10, it has to convert from base 2. Part of the conversion includes rounding.
As we know, 1/6 = 0.1666 (repeating), decimal and double can not represent repeating numbers, they are calculated when assigned to. Since they are built from different backing data structures they represent a different set of possible numbers and round differently in some cases.
For this code:
Console.WriteLine(notQuiteWholeD < 1.0); // Prints: True. Why?
Since notQuiteWholeD is 0.99999999999999989 it prints true.
I'm not going to cover how the double and decimal work behind the scenes but here is some reading material if you're interested.
Double-precision floating-point format
SO - Behind the scenes, what's happening with decimal value type in C#/.NET?
Decimal floating point in .NET

Fix numbers count after point for decimal

How I can fix digits count after point for decimal type WITHOUT ROUNDING
in C#.
For example:
decimal coords = 47.483749999999816; (part of geographic coordinates)
I need to stay only 6 digits after point: 47.483749
You might use
decimal truncated = Math.Truncate(coords * 1000000m) / 1000000m;
or maybe
decimal truncated = Math.Truncate(coords / 0.000001m) * 0.000001m;
Of these two, I have come to prefer the latter. The number 0.000001m is represented internally as the mantissa 1 together with the scale 6, so dividing and multiplying by it is effectively just a move of the decimal point by six places.
The method where you multiply by 0.000001m in the end tends to give a result with exactly six digits after the decimal point, even with trailing zeroes if necessary. For example coords = 1.23m will lead to truncated being 1.230000m.
As noted in the comments, the above techniques will both lead to an OverflowException if coords is too huge (more than one millionth of the decimal.MaxValue).
For completeness, here are a couple of other solutions:
decimal truncated = coords - coords % 0.000001m;
This seems to work fine. The number of trailing zeroes will not always be correct though, but I believe it gives the same result wrt. the == operator (or decimal.Equals).
Finally, you can use:
decimal truncated = Math.Round(coords - 0.0000005m, 6, MidpointRounding.AwayFromZero);
but that works only for non-negative coords.
If what you really need is a formatted string, call a ToString overload on truncated. But beware, as I indicated, of trailing zeroes. If you use .ToString("F6") there should always be exactly six decimals.
Of course you can also use string manipulation for the truncating. It would go something like:
string coordsString = coords.ToString(CultureInfo.InvariantCulture);
int idxOfPoint = coordsString.IndexOf(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator);
if (idxOfPoint != -1 && coordsString.Length > 6 + 1 + idxOfPoint)
coordsString = coordsString.Remove(6 + 1 + idxOfPoint);
// coordsString is now truncated correctly.
// If you need a decimal, use decimal.Parse(coordsString)
Very tricky, and not suggested, but I don't really know what solution are you looking for (since you're avoiding rounding):
double k = 47.483749999999816;
k = double.Parse(Regex.Replace(k.ToString(), #"(\d+\.\d{6})(.\d+)", x => x.Groups[1].Value));
Console.WriteLine(k);
that prints
47.483749
You can use
decimal.Round( 47.483749999999816, 6)

Evaluate if two doubles are equal based on a given precision, not within a certain fixed tolerance

I'm running NUnit tests to evaluate some known test data and calculated results. The numbers are floating point doubles so I don't expect them to be exactly equal, but I'm not sure how to treat them as equal for a given precision.
In NUnit we can compare with a fixed tolerance:
double expected = 0.389842845321551d;
double actual = 0.38984284532155145d; // really comes from a data import
Expect(actual, EqualTo(expected).Within(0.000000000000001));
and that works fine for numbers below zero, but as the numbers grow the tolerance really needs to be changed so we always care about the same number of digits of precision.
Specifically, this test fails:
double expected = 1.95346834136148d;
double actual = 1.9534683413614817d; // really comes from a data import
Expect(actual, EqualTo(expected).Within(0.000000000000001));
and of course larger numbers fail with tolerance..
double expected = 1632.4587642911599d;
double actual = 1632.4587642911633d; // really comes from a data import
Expect(actual, EqualTo(expected).Within(0.000000000000001));
What's the correct way to evaluate two floating point numbers are equal with a given precision? Is there a built-in way to do this in NUnit?
From msdn:
By default, a Double value contains 15 decimal digits of precision, although a maximum of 17 digits is maintained internally.
Let's assume 15, then.
So, we could say that we want the tolerance to be to the same degree.
How many precise figures do we have after the decimal point? We need to know the distance of the most significant digit from the decimal point, right? The magnitude. We can get this with a Log10.
Then we need to divide 1 by 10 ^ precision to get a value around the precision we want.
Now, you'll need to do more test cases than I have, but this seems to work:
double expected = 1632.4587642911599d;
double actual = 1632.4587642911633d; // really comes from a data import
// Log10(100) = 2, so to get the manitude we add 1.
int magnitude = 1 + (expected == 0.0 ? -1 : Convert.ToInt32(Math.Floor(Math.Log10(expected))));
int precision = 15 - magnitude ;
double tolerance = 1.0 / Math.Pow(10, precision);
Assert.That(actual, Is.EqualTo(expected).Within(tolerance));
It's late - there could be a gotcha in here. I tested it against your three sets of test data and each passed. Changing pricision to be 16 - magnitude caused the test to fail. Setting it to 14 - magnitude obviously caused it to pass as the tolerance was greater.
This is what I came up with for The Floating-Point Guide (Java code, but should translate easily, and comes with a test suite, which you really really need):
public static boolean nearlyEqual(float a, float b, float epsilon)
{
final float absA = Math.abs(a);
final float absB = Math.abs(b);
final float diff = Math.abs(a - b);
if (a * b == 0) { // a or b or both are zero
// relative error is not meaningful here
return diff < (epsilon * epsilon);
} else { // use relative error
return diff / (absA + absB) < epsilon;
}
}
The really tricky question is what to do when one of the numbers to compare is zero. The best answer may be that such a comparison should always consider the domain meaning of the numbers being compared rather than trying to be universal.
How about converting the items each to string and comparing the strings?
string test1 = String.Format("{0:0.0##}", expected);
string test2 = String.Format("{0:0.0##}", actual);
Assert.AreEqual(test1, test2);
Assert.That(x, Is.EqualTo(y).Within(10).Percent);
is a decent option (changes it to a relative comparison, where x is required to be within 10% of y). You may want to add extra handling for 0, as otherwise you'll get an exact comparison in that case.
Update:
Another good option is
Assert.That(x, Is.EqualTo(y).Within(1).Ulps);
where Ulps means units in the last place. See https://docs.nunit.org/articles/nunit/writing-tests/constraints/EqualConstraint.html#comparing-floating-point-values.
I don't know if there's a built-in way to do it with nunit, but I would suggest multiplying each float by the 10x the precision you're seeking, storing the results as longs, and comparing the two longs to each other.
For example:
double expected = 1632.4587642911599d;
double actual = 1632.4587642911633d;
//for a precision of 4
long lActual = (long) 10000 * actual;
long lExpected = (long) 10000 * expected;
if(lActual == lExpected) { // Do comparison
// Perform desired actions
}
This is a quick idea, but how about shifting them down till they are below zero? Should be something like num/(10^ceil(log10(num))) . . . not to sure about how well it would work, but its an idea.
1632.4587642911599 / (10^ceil(log10(1632.4587642911599))) = 0.16324587642911599
How about:
const double significantFigures = 10;
Assert.AreEqual(Actual / Expected, 1.0, 1.0 / Math.Pow(10, significantFigures));
The difference between the two values should be less than either value divided by the precision.
Assert.Less(Math.Abs(firstValue - secondValue), firstValue / Math.Pow(10, precision));
open FsUnit
actual |> should (equalWithin errorMargin) expected

Categories