Dot Net Thoughts

October 20, 2007

Doubles, Decimals, and Dividing by Zero

Filed under: Misc Thoughts — dotnetthoughts @ 7:02 am
Tags: , , , , , , , ,

“.Net Exception Divide by zero” often comes up as a search criteria when this blog is hit. (I use division by zero a lot when I’m writing about errors and exception handling. It’s easy to create, and it’s easy to understand.) I couldn’t quite figure out exactly why anybody would be querying on it directly. I think I’ve figured it out, though.

Last week, I was typing up a division example for the blog. For whatever reason, I used double instead of decimal for my input parameters and return value.

   private static double Divide(double i, double j) 
      { 
         return (i / j); 
      }

When I passed in values of 5 and 0 for this method, I was expecting a divide by zero exception. Instead, my console app ran just fine and printed the word Infinity on my screen. I was totally caught off guard by this result. If you pull out an old calculus book, you’ll find that mathematicians often will say that the value of 1/x, as x approaches 0, is infinity (a standard limit), but that 1/0 is undefined. (You can’t take one object and break it into groups of zero.)

When I run the same code above using decimals, instead of doubles, I get a divide by zero exception. This is what I would expect.

With a little bit of thought and poking around, I think that there is a method behind the madness.

Doubles are floating point types. These types are specifically engineered never to throw an exception. Instead, they return values such as infinity, positive infinity, negative infinity, and not a number. Why?

I suspect that the reason has to do with precision. The double type supports numbers as small as +-5*10-324 and as large as +-1.7*10308. Its precision, however is only 15 to 16 digits. Due to the way that floating points are handled, you can’t ever be totally sure exactly what value your Double contains for very large or small numbers. For example, the following code prints out a value of 9.99988867182683E-321 in my output window when I run it:

   private static void DoublePrecision()  
   { 
       double double1 = 1 * Math.Pow((double)10, (double)-320); 
       Trace.WriteLine("double1: " + double1.ToString()); 
   }

This loss of precision means that the compiler itself can’t determine whether a value truly is zero, or if it is a really small number quite close to zero. Mathematically, 1/(10-324)2 is 1/10-648. If the first equation were plugged into .Net, the the result wouldn’t be infinity, but it would be greater than the double can deal with. The convention seems to be to return infinity for my initial divide by zero question.

Decimals, on the other hand, support numbers as small as +-1*10-28 and as large as +-7.9*1028. A decimal’s precision is 28 to 29 significant digits. Since the precision is equal to the exponential power of the range supported, the compiler knows the value it holds in the register is accurate, at least to defined precision. If it thinks it has a zero, it actually has a zero, and can safely throw a DivideByZero error if you try to use it to divide. This added precision is why Microsoft encourages decimals for financial and scientific values.

I’m making some educated guesses behind the thinking of the writers of the IEEE 754 standard, which defines floating types, but I don’t suspect I’m too far off. Let me know if you have any insights!

Good luck and code safe!

Mike

Advertisements

6 Comments »

  1. Hey mike great stuff… Did you happen to figure out how to fix this besides changing the type?? I used an if statement to throw a message when the denominator equals zero, but the infinity still shows up. Ive tried clearing it as well but that doesn’t work. Any help would be great. Thanks

    Comment by Shannon Smith — September 13, 2008 @ 1:46 pm | Reply

  2. Shannon–

    Thanks for the comment. I don’t think there is a way to “fix” this, as it seems that the double works this way by design. If you don’t want to switch the type, than manually checking the denominator is probably the way to go.

    Incidentally, I would check the denominator, even if I were using a decimal. The overhead for doing the check is tiny compared to the overhead of firing up the Exception objects.

    Happy Coding!

    Comment by dotnetthoughts — September 13, 2008 @ 3:08 pm | Reply

  3. Module divzero
    Sub Main()
    Dim n1, n2 As Integer
    Dim op As Char

    Console.WriteLine(“Enter the two numbers and the operator”)

    n1 = Console.ReadLine
    n2 = Console.ReadLine
    op = Console.ReadLine

    Console.WriteLine(“Division ——>” & (n1 / n2))
    Console.ReadLine()
    End Sub
    End Module

    #########################################################

    I am beginner in .net programming. I am facing the same problem here. You said that using “double” gives the output as “infinity”. But I used “integer” ( which is decimal, I hope so) and I got the same one, “infinity”. So please help me out.

    Comment by Sriharsha — December 10, 2008 @ 5:10 pm | Reply

  4. Sriharsha–

    I’m not a VB.Net guru, but it looks like this is a difference in the way the VB compiler and the C# compiler treat code.

    I wrote two simple programs and opened them up in ILDASM.

    C#

    static void Main(string[] args)
    {
    int n1 = 5;
    int n2 = 0;
    int result = n1 / n2;
    }

    VB.Net

    Sub Main()
    Dim n1 As Integer = 5
    Dim n2 As Integer = 0
    Dim result As Integer = n1 / n2
    End Sub

    They appear to do the same thing, but under the covers, they don’t. When I run ILDasm against C#, I get this as a partial result.

    .method private hidebysig static void Main(string[] args) cil managed
    {
    .entrypoint
    // Code size 10 (0xa)
    .maxstack 2
    .locals init ([0] int32 n1,
    [1] int32 n2,
    [2] int32 result)
    IL_0000: nop
    IL_0001: ldc.i4.5
    IL_0002: stloc.0
    IL_0003: ldc.i4.0
    IL_0004: stloc.1
    IL_0005: ldloc.0
    IL_0006: ldloc.1
    IL_0007: div
    IL_0008: stloc.2
    IL_0009: ret
    } // end of method Program::Main

    The both parameters are loaded as integers, and the division is returned as an integer.

    In VB.Net, however, the code disassembles to this:

    .method public static void Main() cil managed
    {
    .entrypoint
    .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
    // Code size 19 (0x13)
    .maxstack 2
    .locals init ([0] int32 n1,
    [1] int32 n2,
    [2] int32 result)
    IL_0000: nop
    IL_0001: ldc.i4.5
    IL_0002: stloc.0
    IL_0003: ldc.i4.0
    IL_0004: stloc.1
    IL_0005: ldloc.0
    IL_0006: conv.r8
    IL_0007: ldloc.1
    IL_0008: conv.r8
    IL_0009: div
    IL_000a: call float64 [mscorlib]System.Math::Round(float64)
    IL_000f: conv.ovf.i4
    IL_0010: stloc.2
    IL_0011: nop
    IL_0012: ret
    } // end of method Module1::Main

    See the conv.r8s in there, and also the call to System.Math that is rounding to a float64? For whatever reason, VB.Net has decided that divisions should be done with floats. I’ve stated in other posts that C# and VB.Net are not as equivalent as Microsoft says they are, and this is another great example.

    An erie twist on this is the errors that are raised. Because of the way these variables are treated, C# (correctly) returns a divide by zero error, while VB.Net throws an arithmatic overflow!

    Comment by dotnetthoughts — December 11, 2008 @ 7:56 am | Reply

  5. Pretty section of content. I just stumbled upon your website and in accession capital to assert that
    I get in fact enjoyed account your blog posts.
    Any way I will be subscribing to your augment and even
    I achievement you access consistently rapidly.

    Comment by raspberry ketone — April 22, 2013 @ 4:29 pm | Reply

  6. It’s great that you are getting thoughts from this article as well as from our discussion made here.

    Comment by green coffee diet — April 23, 2013 @ 7:01 am | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: