Category Archives: Visual Basic

Floating point follies followup

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.

Floating point follies

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…

MSDN chat transcript posted

MSDN has posted a transcript of the chat that I and other members of my team had a few weeks ago about VB.NET language design. It ended up being much more about future features than past decisions, but that’s to be expected. It was a fun chat for us, and we hope to be able to do another one in the not-too-distant future!

Ask a Language Designer

There’s one other new thing on the site that I wanted to call out on in its own entry. Akin to an idea C# has used on their GDN site, in the “Links” section to the right there is now a link to the Ask a Language Designer page. You can use this page to submit questions that you’d like answered about any particular subject, and I’ll do my best to post some answers to the best questions on this blog. The disclaimer on that page is worth quoting, though, so here it is:

Questions will be answered at my discretion. Because of limitations of time, blog space, non-disclosure of future plans or the suitability of the question, there is no guarantee that a question will be answered. This forum is intended for questions regarding the design of the language, and not as a product support forum. All responses to questions will be made on the blog.

This is going to be a bit of an experiment, so please bear with us and don’t get too upset if it doesn’t work out for some reason! But I’m thinking we’ll be able to get some interesting questions answered…

Accessing shared members from instances

OK, quick quiz. And, yes, this is a trick question. Without looking at VS, what do you expect the following code to print?

Public Sub Foo()
Dim s1, s2 As String

s1 = "abc"
s2.Copy(s1)
Console.WriteLine(s2)
End Sub

Yes, that’s right, it prints nothing. Wha….? Because String.Copy is a shared function that takes a string and returns a copy of it. It has no effect on the instance that it’s being called on.

Another quick quiz. And, yes, this is also a trick question. Without looking at VS, what do you expect the following code to print?

Public Class Foo
Public Shared Baz As Integer = 10
End Class

Module Test
Public Function Bar() As Foo
Console.WriteLine("abc")
Return New Foo()
End Function

Sub Main()
Dim y As Integer

y = Bar().Baz
End Sub
End Module

Yes, that’s right, it prints nothing. Wha….? Since Baz is a shared member, we do not need to evaluate the qualifying instance expression, so we don’t. So Bar is never called.

In general, it is a bad idea to call shared members off of instance expressions for these two reasons. Shared members cannot affect instances directly, so calling shared methods or changing shared properties/fields off of an instance may give surprising results. And because we do not evaluate expressions that qualify shared members, you won’t get side effects where you might expect it. (This has bitten even a few members of the VB team themselves.)

A legitimate question then is, if it’s a bad idea, why do we allow it in the first place? Unfortunately, the original logic has been lost to history – my grey matter no longer contains the memory of when and why we decided to allow this. One possible reason is that disallowing shared member access off of an instance expression causes problems in a common situation:

Enum Color
Red
Blue
Green
End Enum

Class Control
Public Color As Color = Color.Red
End Control

With just the simple rule that shared members cannot be accessed off of instances, the field declaration in Control would be in error. This is because the Color in Color.Red will bind first to the field Control.Color, which is an instance field. Which means you can’t access shared members off of it, and enum members are shared. C# fixes this problem by introducing a special rule: in a context where the name of a variable is the same as the name of its type, you can access shared instances off of an instance. As it turns out, we had to introduce this rule anyway late in VS 2002 for the following related situation:

Class Control
Public Color As Color

Public Shared Sub Bar(ByVal SomeControl As Control)
SomeControl.Color = Color.Red
End Sub
End Class

In this case, the Color in Color.Red still binds to the instance field, but you run afoul of the rule that says that you can’t access instance members in a shared context.

Whatever the original logic, it’s become clear that this is not a good design and that we made a mistake in allowing it. (Yes, Virginia, we do make occasionally make mistakes around here.) I encourage people to avoid using it whenever possible. Unfortunately, the compiler doesn’t really help you on this today, but it’s something we’re be helping you more with in the future.

There are two kinds of people in this world…

…those who think Edit and Continue is a good idea and those who don’t. I still haven’t gotten to answering Franz‘s original question, but a recent entry he made does start to address the question.

I totally understand what he’s saying when he talks about the fact that Edit and Continue allows for some very bad programming habits. I spent a few summers teaching teenagers to program, and I got to see first hand some of the ways that bad habits can derail programmers, especially new programmers. From a theoretical standpoint, it is infinitely preferable for programmers to follow the well-worn steps to programming nirvana: understand the problem, design the program structure, implement the program, test the program, fix the bugs, ship the program. In that order.

And yet… The reality is that life is messy and does not always conform to theoretical norms. I am a skilled programmer who works on a major product produced by a large corporation. But even I have lots of times where the code that I write only needs to be “good enough” and not brilliant. I’m not talking about the VB compiler here – obviously, that has to be brilliant – but about all the side projects and throwaway tools and prototypes and test cases that I work on in my day to day life. It doesn’t matter that they’re well designed, only that they work.

And that’s what makes Edit and Continue so wonderful. When I need to bang something out rapidly, I can design the application on the fly. I outline the application, hit F5 and I’m off, writing the code as I go along. If I made a mistake, no problem, I can fix that on the fly without having to restart the entire application. If I’m working on something that really needs to be polished up later, that’s cool – I can do that later. But for now, I’m getting my work done and I’m doing it without all the theoretical design overhead that I really don’t need at that moment. And there are a lot of people in this world who just need to get their work done like this and don’t need to produce well-designed, meticulously thought-out applications. That’s why Edit and Continue is such a big feature for them.

(I’ll also add that I suspect that many, but certainly not all, developers who do not want Edit and Continue haven’t really tried it. Even for people who think fully through their design and eschew debugging on the fly, there is always those times when you hit an exception 10 minutes into running your application and you realize that you just forgot to initialize some stupid variable. If only you could just fix that one damn line and not have to re-run the application again for 10 minutes… If only…)

Anyway, like I said, I’ll freely admit that EnC can enable people to slide by with some pretty bad habits. But if people find it useful, why not do the feature and allow people to decide for themselves whether they’re going to have bad habits? The purpose of a tool is not to make people use it in the “correct” way, the purpose of a tool is to assist people in getting something done. That means that just because someone might misuse a feature does not, in and of itself, mean that you shouldn’t do it. This is really a core philosophy of VB: whatever it takes to give you the shortest path between your idea and a completed application. That’s RAD.

Whidbey and Orcas roadmap

Thanks to Scott and Sean for pointing out that MSDN has published a high-level roadmap for Whidbey and Orcas. I’m planning at some point in the near to talk a little more about some of the details of Whidbey features, especially generics. Not because I have an urge to blab secrets but because I think there’s value in letting people know where things are headed, and even get a chance to get some feedback.

MustOverride and line orientation

One of the side projects that I’m working on in addition to this weblog is a managed scanner and parser for the Visual Basic .NET language. I started on the project because I’d really like to write some project analysis tools that I could use to determine things about how people use the language, but as it’s gone on I’m hoping to also release it into the community as a sample. (One of the wrinkles is that I’m “writing” the managed parser by looking at the existing unmanaged parser, so it’s not like the code I’m writing is completely of my own invention, although it is largely so.) I think that the more that people can work with a language in an automated way, and the more tools people can write for a language, the better it is for people who use that language. Anyway, we’ll just have to see. I still have to finish it first…

After I finished the scanner (scanners are easy), I started parsing expressions and have been working my way up the parse tree hierarchy. I’ve recently reached type members and, in particular, methods. An interesting thing is the way that VB’s line orientation interacts with MustOverride methods when parsing. In C#, parsing methods is pretty simple because after a method header you can see either a semicolon or an open curly brace. If it’s the former, then you’ve got an abstract method declaration; if it’s the latter, then you’ve got a concrete method declaration. Whether or not you specified abstract as a modifier on the declaration is really something for declaration semantics to sort out later on. In VB, though, the following fragment is ambiguous if you don’t look at the modifiers:

<modifiers> Sub Foo()
Dim Bar As Integer

In other words, once you’ve gotten through the method header, the next line starts with Dim, which could either mean that Foo was a MustOverride method and Bar is a field, or that Foo was a concrete method and Bar is a local. To figure out which is which, you absolutely have to look at the modifiers to see if MustOverride is there. This is the kind of thing that drives formal grammar writers nuts because it means you have to hork up your grammar productions to make it all come out right. It doesn’t make such a big deal, though, if you hand-code your parser, which is what we do for VB. (Hand-coded parsers vs table-driven parsers seems to be one of those religious arguments that language people get into. Let me just say I don’t take a formal position on the question.)

Interestingly, there are at least a few other places in the language (esp. in terms of object creation expressions versus array creation expressions) where things get complicated like this. But overall, it’s been pretty smooth sailing. I’ll let everyone know as the project progresses.