1

I wrote this simple program just to show a little problem I have operating with double.

namespace WpfApplication3
{
   public partial class MainWindow : Window
   {
        public MainWindow()
        {
            InitializeComponent();

            double a = 110.0;
            double b = a / 100.0;
            double c = Math.Round(b, 2);
            if ((c % 0.05) == 0)
                Console.WriteLine("Good news!");
            else
                Console.WriteLine("Bad news!");
        }
   }
}

What I am doing wrong, if running the program, on console I see "Bad news!".... Thank you for your opinion, regards

sujith karivelil
  • 28,671
  • 6
  • 55
  • 88
G.Zana
  • 11
  • 7
  • 2
    That's a floating point error caused by `double`. Use `decimal` instead http://stackoverflow.com/questions/588004/is-floating-point-math-broken – fubo Jul 08 '16 at 06:40
  • Thank you, I will follow your hint. – G.Zana Jul 08 '16 at 07:28
  • Neither the quotient `110.0 / 100.0` (mathematically eleven tenths) nor the quantity `0.05` (mathematically one twentieth) can be represented exactly as a finite binary expansion. When you write them out in binary, there is a block of digits (after the [point](https://en.wikipedia.org/wiki/Radix_point)) that repeats forever. The `double` type will have to truncate this infinite sequence of bits at some point. Therefore, calculations will not be exact. – Jeppe Stig Nielsen Jul 08 '16 at 07:55

3 Answers3

3

Double is not accurate, you can't represent all numbers exactly using float/double and this leads to unexpected behavior. So you need to use decimal:

decimal a = 110.0m;
decimal b = a / 100.0m;
decimal c = Math.Round(b, 2);
if ((c % 0.05m) == 0)
    Console.WriteLine("Good news!");
else
    Console.WriteLine("Bad news!");

Output:

Good news!

The decimal type has a larger precision than any of the built-in binary floating point types in .NET, although it has a smaller range of potential exponents. Also, many operations which yield surprising results in binary floating point due to inexact representations of the original operands go away in decimal floating point, precisely because many operands are specifically represented in source code as decimals. However, that doesn't mean that all operations suddenly become accurate: a third still isn't exactly representable, for instance. The potential problems are just the same as they are with binary floating point. However, most of the time the decimal type is chosen for quantities like money, where operations will be simple and keep things accurate. (For instance, adding a tax which is specified as a percentage will keep the numbers accurate, assuming they're in a sensible range to start with.) Just be aware of which operations are likely to cause inaccuracy, and which aren't.

Read this article for more.

Zein Makki
  • 29,485
  • 6
  • 52
  • 63
2

Learn to use breakpoints and debug your code so you can learn how to solve your own problem (some cases). Let's go through your application..

    double a = 110.0;
    double b = a / 100.0;
    double c = Math.Round(b, 2); // c = 1.1
    if ((c % 0.05) == 0) // 1.1 % 0.05 = 0.000000000000000027755575615628914 which is not equal to 0.. thus it is false..
        Console.WriteLine("Good news!");
    else
        Console.WriteLine("Bad news!"); // Ooooh it's false, Bad news!

UPDATE:

But if you change to..

    double a = 110.0;
    double b = a / 100.0;
    double c = Math.Round(b, 2); // c = 1.1
    if (Math.Round(c % 0.05) == 0) // 1.1 % 0.05 = 0.0 which is equal to 0.. thus it is true..
        Console.WriteLine("Good news!"); // Hurray!
    else
        Console.WriteLine("Bad news!");

Also, using the decimal data type as others suggest will yield the same result.

jegtugado
  • 5,081
  • 1
  • 12
  • 35
  • Thank Ephraim, in fact if a set a break point on 'if' the variable 'c' is equal to = 1.1000000000000001 (this is strange after a Round, or not?), secondly if after the Round function I insert this "double d = c / 0.05 = 22.0", I have no remainder....(?) – G.Zana Jul 08 '16 at 07:24
  • That's weird, I re-evaluated and mines show c is 1.1 nothing more and the result is exactly as I posted above. I get 22.0 for d and it has no remainder. – jegtugado Jul 08 '16 at 07:31
1

When working with float and double avoid using == and != comparisons because of round up errors, compare with tolerance, instead:

   ...

   double tolerance = 1e-6;

   // Comparison with tolerance: difference (if any) is less than tolerance
   if (Math.Abs(c % 0.05) < tolerance)
     Console.WriteLine("Good news!");
   else
     Console.WriteLine("Bad news!");
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215