Integer Arithmetic in VBScript, Part Two

Here's another recent question I've received on bit twiddling in VBScript:

You discussed the issues with interpreting error results that come back interpreted as signed longs last year.

Suppose we have a large unsigned long value, something like E18F4994. VBScript returns this value as -510703212. How can we go from this to the "representation" that a C user would get, the value 3784264084? Or given a string containing that representation, "3784264084", how can a VBScript user work out the hex?

Indeed, I did discuss that last year,

here. The key to solving the problem in JScript and VBScript is the same. Consider a 32 bit pattern interpreted as a signed integer and an unsigned integer. If the high bit is zero, both interpretations agree. If the high bit is one then the signed interpretation is equal to the unsigned interpretation minus 2^32.  Or, stated the other way, the representation of a negative number is determined by subtracting it from 2^32 and then using the representation of that unsigned number.

This makes constructing the conversion functions you want pretty easy:

Function ReinterpretSignedAsUnsigned(ByVal x)
  If x < 0 Then x = x + 2^32
  ReinterpretSignedAsUnsigned = x
End Function

Function UnsignedDecimalStringToHex(ByVal x)
  x = CDbl(x)
  If x > 2^31 - 1 Then x = x - 2^32
  UnsignedDecimalStringToHex = Hex(x)
End Function


&He18F4994 ' -510703212
print ReinterpretSignedAsUnsigned(&hE18F4994) '
print UnsignedDecimalStringToHex("3784264084") ' "E18F4994"

You might wonder why it is that we use such a goofy way to represent negative integers as "if the high bit is set then interpret it as an unsigned integer but subtract 2^32". The

"obvious" way to represent negative integers is to declare that the high bit is the "sign bit" and then just have a 31 bit integer. In that system

00000000000000000000000000000111 = &h00000007 = 7
10000000000000000000000000000111 = &h80000007 = -7

very simple and straightforward, right? However, that system has one minor problem, and

the system we actually use has a major advantage.

The minor problem is that in this system there are two zeros -- a "positive zero" and a "negative zero", which is darn weird. That's a pretty minor problem though -- a problem shared, in fact, by floating point numbers. A 64 bit float consists of a 52 bit unsigned integer, a sign bit, and

eleven bits of exponent; if you can twiddle the bits then it's possible to represent +0 and -0 differently in a float, though of course it is silly to do so. (The exact details of how zeros, infinities, nans and denormals work in floating point arithmetic is a subject for another day.)

The major advantage of the "subtract off

2^32" representation for negative numbers becomes apparent when you notice this interesting fact: adding 2^32 to a 32 bit integer is a no-op. You'd go to add the 33rd bit, and there isn't one there, so nothing happens.

Think about that in the context of implementing subtraction. You want to calculate 10 - 3:

10 - 3

= 0000000A + (-3)
0000000A + (-3) + 2^32, since in 32 bit arithmetic, adding 2^32 is a no-op.
= 0000000A + (2^32 - 3),
but that is

representable as a 32 bit integer
= 0000000A + FFFFFFFFD
= 00000007, because we throw away the high bit that doesn't fit into the 32 bit integer

Get it? If you represent negative integers this way then you don't have to build another circuit on your chip to handle subtraction. You just build a circuit that handles unsigned integer addition. Integer subtraction and unsigned integer addition are the same operation at the bit level.

Comments (13)

  1. DrPizza says:

    " if you can twiddle the bits then it’s possible to represent +0 and -0 differently in a float, though of course it is silly to do so"

    Except it’s a necessary and useful thing to do; complex numbers make good use of them. A negative zero allows the following identities to hold true:

    sqrt(conjugate(z)) = conjugate(sqrt(z))

    log(conjugate(z)) = conjugate(log(z))

  2. Orbifold says:

    Waaaay back in Windows 3.1, the format for help files stored integers (for margin indents and the like) in the most bizarre format I’ve ever seen: threes-complement signed integers, with a reversed sign bit.

    In other words, if you had an 8-bit field then

    10000010 meant "2"

    10000001 meant "1"

    10000000 meant "0"

    01111111 meant "-2", yes, "-2"

    01111110 meant "-3", and so on.

    There was simply no way to represent the integer -1 in a Windows help file. Did you want to set the paragraph indent in your help file to -10 twips for some reason? Too bad: paragraph indents were stored in tens of twips, so the Windows help compiler would reset that particular indent to 0 twips.

  3. Dave says:

    Nice. I had solved the problem of getting unsigned numbers in Javascript this way:

    function ReinterpretSignedAsUnsigned(v)

    { return (v >>> 1) * 2 + (v&1) }

    Same result but not as elegant.

  4. Norman Diamond says:

    > Indeed, I did discuss that last year, here.

    "here" says:

    > .Text – Application Error!

    > Details

    > NullReferenceException

    > Object reference not set to an instance of an object.

    By the way, even though you showed (and I thank you) that in cases where programmers want unsigned longs in VB it really does turn out to be not so hard to produce printable strings showing their values in decimal, still don’t you think that conversion to double precision floating is slightly sub-optimal? VB has unsigned bytes; why haven’t unsigned longs ever been added?

    > (The exact details of how zeros, infinities,

    > nans and denormals work in floating point

    > arithmetic is a subject for another day.)

    You misspelled "decade".

  5. Mat Hall says:

    "You might wonder why it is that we use such a goofy way to represent negative integers…"

    Are there actually people reading this who are unaware of two’s complement?

  6. Eric Lippert says:

    Dude, contain the condescension.

    A considerable majority of script developers have no computer science degree. Many have no formal training in programming whatsoever — their entire knowledge of programming languages comes from reading existing scripts and puzzling them out, maybe with a copy of "Learn VBScript in 21 Days" at hand. They have no idea what "twos complement" means. Why would they? It’s an implementation detail that is usually abstracted away.

  7. Mat Hall says:

    Sorry, didn’t mean to be condescending; it was genuine surprise! (I don’t have a comp sci degree either, by the way.) I was also a bit surprised that in your explanation you didn’t mention its name…

  8. Norman Diamond says:

    I even had a colleague who was a software engineer, more highly paid than me and seemed to be deserving of it, who participated in development of debuggers and such stuff … and one day it turned out that she wasn’t aware of two’s complement. That was sure startling. I don’t think I ever asked what she had studied in university, but that wasn’t the moment to ask anyway.

    There are quite a lot of engineers who don’t understand that floating point has less than perfect precision in hardware and in ordinary programming languages, and they don’t know how to deal with the fact. Even someone who did a C-style printf to write numbers with 3 digits after the decimal point and then read them back later didn’t understand that the results didn’t match the original numbers before they were written. I don’t know if this is harder or easier than two’s complement, and in these cases maybe the engineering skills aren’t in the computer area, but still… You have to learn to be patient with some of these people.

    Of course when someone has studied the matter and still manages to be unaware of it, or when someone markets a product and it fails and they renege on their warranties because they don’t understand it, then it’s different.

  9. A month ago I was discussing some of the issues in integer arithmetic , and I said that issues in floating

  10. r0ck says:

    Might be kinda dumb, but I learned VBScript by making keyboard macros. I used this website to get started

Skip to main content