Cannot use parentheses when calling a Sub

Every now and then someone will ask me what the VBScript error message “Cannot use parentheses when calling a Sub” means. I always smile when I hear that question. I tell people that the error means that you cannot use parentheses when calling a Sub — which word didn’t you understand?

Of course, there is a reason why people ask, even though the error message is perfectly straightforward. Usually what happens is someone writes code like this:

Result = MyFunc(MyArg)
MySub(MyArg)

and it works just fine, so they then write

MyOtherSub(MyArg1, MyArg2)

only to get the above error.

Here’s the deal: parentheses mean several different things in VB and hence in VBScript. They mean:

1) Define boundaries of a subexpression: Average = (First + Last) / 2
2) Dereference the index of an array: Item = MyArray(Index)
3) Call a function or subroutine: Limit = UBound(MyArray)
4) Pass an argument which would normally be byref as byval: in Result = MyFunction(Arg1, (Arg2)) , Arg1 is passed by reference, Arg2is passed by value.

That’s confusing enough already. Unfortunately, VB and hence VBScript has some weird rules about when #3 applies. The rules are

3.1) An argument list for a function call with an assignment to the returned value must be surrounded by parens: Result = MyFunc(MyArg)
3.2) An argument list for a subroutine call (or a function call with no assignment) that uses the Call keyword must be surrounded by parens: Call MySub(MyArg)
3.3) If 3.1 and 3.2 do not apply then the list must not be surrounded by parens.

And finally there is the byref rule: arguments are passed by reference when possible but if there are “extra” parens around a variable then the variable is passed by value, not by reference.

Now it should be clear why the statement

MySub(MyArg)

is legal but

MyOtherSub(MyArg1, MyArg2)

is not. The first case appears to be a subroutine call with parens around the argument list, but that would violate rule 3.3. Then why is it legal? In fact it is a subroutine call with no parens around the arg list, but has parens around the first argument! This passes the argument by value. The second case is a clear violation of rule 3.3, and there is no way to make it legal, so we give an error.

These rules are confusing and silly, as the designers of Visual Basic .NET realized. VB.NET does away with this rule, and insists that all function and subroutine calls be surrounded by parens. This means that in VB.NET, the statement MySub(MyArg) has different semantics than it does in VBScript and VB6 — this will pass MyArg by reference in VB.NET but by value in VBScript/VB6. This was one of those cases where strict backwards compatibility and usability were in conflict, and usability won.

Here’s a handy reference guide to what’s legal and what isn’t in VBScript: Suppose x and y are variables, f is a one-argument procedure and g is a two-argument procedure.

' to pass x byref, y byref:
f x
Call f(x)
z = f(x)
g x, y
Call g(x, y)
z = g(x, y)

' to pass x byval, y byref:
f(x)
Call f((x))
z = f((x))
g (x), y
g ((x)), y
Call g((x), y)
z = g((x), y)

The following are syntax errors:

Call f x
z = f x
g(x, y)
Call g x, y
z = g x, y

Ah, VBScript. It just wouldn’t be the same without these quirky gotchas.

Why does JavaScript have rounding errors?

Try this in JScript:

window.alert(9.2 * 100.0);

You might expect to get 920, but in fact you get 919.9999999999999. What the heck is going on here?

Boy, have I ever heard this question a lot.

Well, let me answer that question with another question. Suppose you did a simple division, say

window.alert(1.0 / 3.0);

Would you expect an infinitely large window that said “0.33333333333…” with an infinite number of threes, or would you expect ten or so digits?

Clearly you’d expect it to not fill your computer’s entire memory with threes. But that means that I must ask why are you willing to accept an error of 0.00000000000333333… in the case of dividing one by three but not willing to accept a smaller error of 0.000000000001 in the case of multiplying 9.2 by 100?

The simple fact is that all floating-point arithmetic accumulates tiny errors. In JavaScript and many other languages, arithmetic which results in numbers that cannot be represented by a small number of powers of two will result in these tiny errors.

Let’s look at this case a little closer, where we’re trying to multiply 9.2 by 100.0.

100.0 can be exactly represented as a floating point number because it’s a small integer. But 9.2 can’t be — you can’t represent 46/5 exactly in base two any more than you can represent 1/3 exactly in base ten. So when converting from the source code text “9.2” to the internal binary representation, a tiny error is accrued.

However, it’s not all bad; the 64 bit binary number which represents 9.2 internally is (a) the 64 bit float closest to 9.2, and (b) the algorithm which converts back and forth between strings and binary representation will round-trip — that binary representation will be converted back to “9.2” if you try to convert it to a string.

Now we go and throw a wrench in the works by multiplying by one hundred.

That’s going to lose the last few bits of precision because we just multiplied the accrued error by a factor of one hundred. The accrued error is now large enough that the string which most exactly represents the computed value is NOT “920” but rather “919.9999999999999”.

The ECMAScript specification mandates that JavaScript display as much precision as possible when displaying floating point numbers as strings.

VBScript does not do this. VBScript has heuristics which look for this situation and deliberately break the round-trip property in order to make this look better. In JavaScript you are guaranteed that when you convert back and forth between string and binary representations you lose no data. In VBScript you sometimes lose data; there are some legal floating point values in VBScript which are impossible to represent in strings accurately. In VBScript it is impossible to represent values like 919.9999999999999 precisely because they are automatically rounded!

Ultimately, the reason that this is an issue is because we as human beings see numbers like 9.2, 100.0 and 920 as “special”. If you multiply 3.63874692874 by 4.2984769284 and get a result which is one-billionth wrong no one cares, but when you multiply 9.2 by 100.0 and get a result which is one-billionth wrong everyone yells at me! The computer doesn’t know that 9.2 is more special than 3.63874692874 — it uses the same lossy algorithms for both.

All languages which use double-precision floating point arithmetic have this feature — C++, VBScript, JavaScript, whatever. If you don’t like it then either get in the habit of calling rounding functions, or don’t use floating point arithmetic, use only integer arithmetic.

Fabulous Adventures!

Welcome! I’m Eric Lippert; I design and implement programming language tools. This is my blog where I talk about programming and programming language design, with occasional diversions about my other various interests.

I wrote this blog from 2003 to 2012 for Microsoft and I am gradually migrating the content to this site; there is a lot of it, the formatting is inconsistent, and it will take some time.

If you’re looking for the original content, it is archived in several locations because Microsoft continues its multi-decade tradition of not knowing how to run a blog server for some reason.

The early part of this blog is mostly about JavaScript and VBScript; the latter part is mostly about C#. If that sounds like fun to you, let’s go have some fabulous adventures in coding!