.comment-link {margin-left:.6em;}

Saturday, February 12, 2005

 

at Last i found Why don't I get an exception when I divide by zero in VB.NET?

of course not everyone will like this explanation coz it is deep technical , but this is the Man Behind VB.NET reply to that question

While talking about exception handling, Roy ran up against a change in behavior from VB6 to VB.NET. In VB6, if you evaluate the expression "x = x / 0", you'll immediately get a divide by zero exception. In VB.NET, if you evaluate the same expression, no exception is thrown. Instead the value of x is now NaN, also known as "Not a Number." In reality, you could get this behavior in VB6 as well - on the Compile tab of the project properties, there's an "Advanced Optimizations" button that includes an option "Remove Floating Point Error Checks." If you check that and make an exe (it doesn't affect F5 in the IDE), then the assignment will work and x will contain the value NaN.

The reason for all this is that floating point numbers (as defined by the IEEE) support a number of special values that aren't usually surfaced to VB programmers. In addition to NaN, a floating point variable can also contain the values positive and negative infinity. Not being a numerical expert, I won't even pretend to explain what these values are for or why the IEEE felt they were important to include as part of the definition of floating point numbers. The bottom line is that they are there. By default in VB6, though, we inserted X86 instructions that checked for the various non-numerical states and would throw an exception if you encountered one. In VB.NET, however, things were complicated by the way that the CLR IL was designed, namely the fact that it doesn't support a really efficient way to check for non-numerical results. In VB 2002, we tried simulating the VB6 behavior by emitting CKFINITE opcodes after each floating point operation, but this absolutely tanked all floating point performance. So we ended up dropping the VB6 behavior, and now floating point division by zero will result in NaN instead of an exception.

A related issue is that most processors' FPU registers can handle floating point numbers with a higher precision than can be stored in the standard .NET Double datatype. As a result, depending on how the CLR JIT compiler enregisters variables and temporary values, it's possible that floating point operations (including comparisons) can be done at differing levels of precision. For example, the following code could print "False" instead of "True":


Dim d1, d2 As Double

d1 = Atn(-1)
d2 = Atn(-1)

If d1 = d2 Then
Console.WriteLine("True")
Else
Console.WriteLine("False")
End If

That's because one of the variables could be left in a FPU register while the other variable is actually stored back on the stack. When you compare the two values, they aren't equal because one of the values was truncated (so it could be stored on the stack) and the other value wasn't. By default, VB6 suppressed this behavior by emitting X86 code to truncate values that were at higher precisions. (You can turn this off on the advanced optimizations page, too, under the option "Allow Unrounded Floating Point Operations.") But the CLR equivalent - inserting CONV.R8 opcodes everywhere a higher precision value might be used - had similar performance problems to the NaN checks.

As a result, VB.NET floating point operations are much "closer to the metal," which is either a good thing or a bad thing depending on your perspective. You definitely have to be a little more aware of what you're doing when dealing with floating point numbers...



In a response to my previous entry, Cory asks:

...how would you solve the comparison of two values if one is still on the stack and the other has been [stored in a register]? In other words, what would be the proper way to handle this type of situation (work around, etc.)?

A good question, and one that I should have addressed. You can get the compiler to insert explicit CONV.R8 opcodes by using the CDbl() conversion operator. So the code example would look like:


Dim d1, d2 As Double

d1 = Atn(-1)
d2 = Atn(-1)

If CDbl(d1) = CDbl(d2) Then
Console.WriteLine("True")
Else
Console.WriteLine("False")
End If

If you look at the code this compiles to, you should see CONV.R8 opcodes after each local is loaded, and that should force the values to both be truncated


Comments: Post a Comment



<< Home

This page is powered by Blogger. Isn't yours?