Math.Round (double, decimal) always returns consistent results

Of course, you should never compare floating point values โ€‹โ€‹that are the result of a calculation for equality, but always use a small tolerance, for example:

double value1 = ... double value2 = ... if (Math.Abs(value1 - value2) < tolerance * Math.Abs(value1)) { ... values are close enough } 

But if I use Math.Round, I can always be sure that the resulting value will be consistent, i.e. will the next Assert always be executed, even if the rounded value is a value that cannot be represented exactly double?

 public static void TestRound(double value1, double value2, int decimals) { double roundedValue1 = Math.Round(value1, decimals); double roundedValue2 = Math.Round(value2, decimals); string format = "N" + decimals.ToString(); if (roundedValue1.ToString(format) == roundedValue2.ToString(format)) { // They rounded to the same value, was the rounding exact? Debug.Assert(roundedValue1 == roundedValue2); } } 

If not, please provide a counterexample.

EDIT

Thanks to the expert for a counterexample created by brute force, which proves that the result is not "consistent" in the general case. This counterexample contains 16 significant digits in a rounded result - it also fails in the same way when it scales in this way:

  double value1 = 10546080000034341D; double value2 = 10546080000034257D; int decimals = 0; TestRound(value1, value2, decimals); 

However, I would also be interested in a more mathematical explanation. Bonus bonuses for any of the more mathematical Stackoverflowers that can perform any of the following actions:

  • Find a counterexample where a rounded result has less than 16 significant digits.

  • Define a range of values โ€‹โ€‹for which the rounded result will always be โ€œconsistentโ€ as defined here (for example, all values โ€‹โ€‹in which the number of significant digits in the rounded result is <N).

  • Provide an algorithmic method for creating counterexamples.

+4
source share
2 answers

Well, that seems like a very technical matter, so I thought brute force could tell us.

I tried the following

 public static void TestRound(double value1, double value2, int decimals) { double roundedValue1 = Math.Round(value1, decimals); double roundedValue2 = Math.Round(value2, decimals); string format = "N" + decimals.ToString(); if (roundedValue1.ToString(format) == roundedValue2.ToString(format)) { // They rounded to the same value, was the rounding exact? if (roundedValue1 != roundedValue2) { string s = ""; } } } private void button1_Click(object sender, EventArgs e) { for (double d = 0, inc = .000001; d < 1000; d += inc) for (int p = 0; p <= 15; p++) TestRound(Math.Pow(Math.Pow(d, inc), 1 / inc), d, p); } 

I set a breakpoint on the string "s" = ";" chcek, when he goes into this section,

and it is entered with the following values

 value1 = 1.0546080000034341 value2 = 1.0546080000034257 decimals = 15 roundedValue1 = 1.0546080000034339 roundedValue2 = 1.0546080000034259 roundedValue1.ToString(format) = 1.054608000003430 roundedValue2.ToString(format) = 1.054608000003430 

I think this is the answer you were looking for?

If not, let me know, so I can check more.

+2
source

Although floating point calculations have limited accuracy and are therefore inaccurate, they are deterministic. Therefore, you should always get the same result if you use the same calculations with the same values โ€‹โ€‹in the same order. Thus, using the same rounding method with the same value will lead to the same result.

The problem with floating point calculations is that the results of two different calculations that mathematically produce the same result (eq Sqrt(x)*Sqrt(x)==x ) can and will most likely differ from for rounding errors inside calculations

+2
source

All Articles