PDA

Click to See Complete Forum and Search --> : The Floating-Point conspiracy


Koralt
Dec 20th, 2000, 11:50 AM
I've gotten so tired with seeing "Single" and "Double" everywhere, I just thought I'd best post on this topic right about now.

Everyone seems to assume that in order to get high data precision they need to use floating-point numbers (Single or Double) because integer and fixed-point numbers (Integer, Long, Currency) have limited precision--in the case of Integers, that limit is 1; for Currency, unless I'm mistaken, it's 1/128 or 1/256. While it's true that fixed-point numbers have limited precision, it must be remembered that floating point numbers do, too!

Take a Single for example. A Single is (iirc) a 4 byte number. It is split into a few parts--the sign and the point position (iirc, these are collectively called the mantissa), and the numerical data. That means that with a Single precision floating point number you have about 3 bytes of numerical data and one byte which determines what each '1' in the numberical data represents (be it 1/10th, 1/100th, 1000, 100000, or anything else). That's one byte *LESS* precision than Long gives you.

Additionally, floating point numbers are *slow*. Yes, there is a coprocessor for them. The coprocessor is designed to work with IEEE floating point numbers, and it does so rather quickly. They main processor, though, is designed to work with Integers, and it does so VERY quickly! I did a test at one point, and, iirc, I found that Integer numbers compute around 100 times faster.

Many programmers have trouble with this, though. "What if I need to do money? How can I do money without a decimal place?" you may ask. Quite easily! Rather than using floating-point numbers and saying that each "1" is a dollar, use an integer number (Long) and say that each "1" is a penny. Or, alternately, use the Currency data type. Currency is preferable to Short or Double as far as speed goes, but not nearly as fast as Long and Integer.

"Well, what if I need to store the position of my character? I need decimal for that -- this isn't a tile based game, you know!" That's a simple problem with a simple solution: rather than saying each "1" is a metre, say it's a centimeter or milimeter.

One final flaw with floating point--the farther you go from the origin (zero), the less precision you have. Think of it like this:

Imagine 000.000 is a basic floating point number. It has six digits of precision.

You could store any of the following:

.000301
1.00001
403.000
99032.1
904890.
9.05030 * 10^20


As you can see, as the number grows, you have less decimal places, until you reach a point where you no longer even have ones, or tens, or 100s. Single and Double both have that problem.

In summary, use Integer and Long whenever possible, making use of Currency only when those don't have enough range.

Also, one final gripe about optimization: most people use Sqr() when it's not necessary. It's a dreadfully slow function, and should be avoided. Take collision for example:

'This code determines whether two circles, C1 and C2 have
' collided

'Slowest:
'Note, I use -\ here as a next line thing.
'to keep it from going too wide. These are
'the same line.
If Sqr((C1.x - C2.x) ^ 2 + (C1.y - C2.y) ^ 2 + -\
(C1.z - C2.z)) < (C1.r + C2.r) Then
'Has collided!


'Medium:
xd = C1.x - C2.x
yd = C1.y - C2.y
zd = C1.z - C2.z
If Sqr(xd * xd + yd * yd + zd * zd) < (C1.r + C2.r) Then
'Has collided!


'Better than either, probably not fastest:
xd = C1.x - C2.x
yd = C1.y - C2.y
zd = C1.z - C2.z
ca = C1.r + C2.r
If (xd * xd + yd * yd + zd * zd) < (ca * ca) Then
'Has collided!


There are a few cases when using floating-point numbers is ok, namely when you're using a VB function that takes a floating-point number as an argument, such as if you *have* to take a Sqr. It takes a relatively long time to convert from floating-point to integer or vice-versa.

Those are just a few ways in which you can speed up your programs ... just thought I'd point 'em out. There are a million, though, if anyone cares.

Oh, one final tip: Avoid division and multiplication whenever possible. Multiplication is faster than division, and division is SLOW. Use integer division -- the backslash -- whenever possible with integers.


b& = c& \ d& 'Not b& = c& / d&


Always try to remember what something actually means when you do it (like, division is just multiple subtractions). That may give you ideas as to how to do something faster.

[Edited by Koralt on 12-20-2000 at 12:54 PM]

HarryW
Dec 20th, 2000, 03:47 PM
Well I just wrote an integer multiplication instruction in microcode, and on a very basic CPU (a rather inefficient one) it took approximately 140 cycles to multiply two 32-bit integer numbers on the stack and write the result to the stack in two 32-bit integers. The point is that an integer add instruction is probably at most three cycles on a modern CPU, but if you're going to be multiplying large numbers then using adds (in a loop) instead of a multiply may be less efficient.

Koralt
Dec 20th, 2000, 05:00 PM
Well, I would rarely advise using, for example, multiple subtractions to emulate division -- that *is* slower -- but there are cases when such knowledge is useful. It's the basis of Bresenham's line drawing algorithm, in fact ...

HarryW
Jan 18th, 2001, 09:30 PM
Just remembered this post, thought I'd add to it.

I recently read that some processors can do floating point operation as fast or faster than integer operations, because they have so much hardware dedicated to it. The big loss of performance comes in when the floating point number has to be truncated and converted to an integer. I haven't tested it so I can't guarantee its truth, but it was mentioned in a book by a reputable author. It did say might be faster.

While I was posting I thought I would mention for anyone that doesn't know that multiplying a number by any integer constant can be achieved using only shifts and adds. Any number can be expressed as the sum of one or more powers of 2. This should be fairly obvious since this is how you store an unsigned integer in binary. For instance, multiplying x by 640 can be achieved by the expression

(x * 640)

but this is also

(x*128 + x*512) or (x*2^7 + x*2^9)

and since multiplication by 2 can be done using left shifts, this is also

(x<<7 + x<<9) <----- C notation

I don't think there are bitwise shift operators in VB, but there are in C and other languages.

Koralt
Jan 23rd, 2001, 04:16 PM
That's right, actually. In many languages that is faster; in VB there is no equivalent function. Because trunctation and conversion of floating<->integer is so slow, you should make sure VB knows what data type your numbers are.

If, for example, you insist on using floating point variables, you don't want to do something like:


Dim x As Single

x = 640
x = x * 4


You want to do:


Dim x As Single
x = 640!
x = x * 4!