...: Marsh Chatter
When is Zero... Not Zero!
One thing that always "gets me", when writing code, is the handling of numbers in data types such as Double or Single (e.g. the .NET floating-point types). Yep, the simple stuff always comes back to haunt you. Let me explain...
I usually pick the larger data types for a "Divide by Zero" example in class (e.g. exception handling section). Yet, the handling of those data types results in behavior that always causes the pregnant pause with a question... "why did it just display Infinity, versus my Exception"? For class - it's great, because we get to explore the cause and effect. For work, it's one of those moments where you just slap yourself...
What's up with this?
The .NET floating point data types are based on IEEE standards, which is that floating point numbers are stored with large significant digit lengths and may not be what you expect -- ergo 0 (zero) may actually be represented in memory as 0.00000000...0001 -- where the ellipsis (...) is lots of zeros (and 1.0 could actually be stored as 0.99999...99999).
The end result is that when you divide by zero with this in-memory representation, you are really performing the Math rule 1/x - where x = a small number; mathematically this division = infinity, and not the exception we expected. For the floating-point data types, it kind of makes the "DivideByZeroException" useless (but wait... it has value with integer types).
For those interested, the following sites have more on this subject...
Not Just Divide By Zero... There's More!
Interestingly - testing these variables to see if they are zero returns "true". The following code example illustrates this. The "conditions" are shown performing an equivalency test. No division takes place.
Dim TheZeroAsVariable As Double = 0.0
Dim TheZeroViaConvert As Double = Convert.ToDouble("0.000")
Dim TheZeroViaParse As Double = Double.Parse("0.000")
Try
If TheZeroAsVariable = 0.0 Then
VarResultsLiteral.Text = "Variable: " & TheZeroAsVariable.ToString
Else
VarResultsLiteral.Text = (5.0 / TheZeroAsVariable).ToString
End If
If TheZeroViaConvert = 0.0 Then
ConvertResultsLiteral.Text = "Convert: " & TheZeroViaConvert.ToString
Else
ConvertResultsLiteral.Text = (5.0 / TheZeroViaConvert).ToString
End If
If TheZeroViaParse = 0.0 Then
ParseResultsLiteral.Text = "Parse: " & TheZeroViaParse.ToString
Else
ParseResultsLiteral.Text = (5.0 / TheZeroViaParse).ToString
End If
StatusLiteral.Text = "Results... 0 is 0 when you test it, but not when you use it."
Catch ex As DivideByZeroException
StatusLiteral.Text = ex.Message
Catch ex As ArithmeticException
StatusLiteral.Text = ex.Message
Catch ex As Exception
StatusLiteral.Text = ex.Message
End Try
Results
The Zero is caught by the condition, not by Exception:
Variable: 0
Convert: 0
Parse: 0
Results... 0 is 0 when you test it, but not when you use it.
Does this ONLY affect Zero?
Now - just so you don't go thinking this only affects zero and that you're safe with other numbers. You're Not! The same issue would affect you if the number was 3, 100, or "bazillon". The floating-point data types will not represent the number "exactly" - hence your math or your conditions may not do what you are expecting.
Additionally, I wrote the example code with both types of converting methods - Convert and Parse, and the declared variable to show you that each of these do not have an affect on the results. It is all about the data type, not the converting method.
So What... do you do?
Pick a different data type.
-
Decimal is recommended by Microsoft for financial calculations.
-
If your result is not dependent on decimal places - use the Byte, Short, or Integer type data types.
-
If you need the additional digits (but not significant decimals) for your calculations, then stick with the Single or Double - just remind yourself to "watch your P's & Q's" with respect to these data types.
Back to writing code...