Floats vs. Doubles

Blitz3D Forums/Blitz3D Beginners Area/Floats vs. Doubles

Rob the Great(Posted 2011) [#1]
I'm aware that Blitz3D only supports floats, but I've never really seen the problem with floats until just now where I'm getting a weird result. As an example:
Local testfloat# = 1.1

While Not KeyDown(1)

   If KeyHit(200)
      testfloat# = testfloat# + 0.1
   EndIf
   If KeyHit(205)
      testfloat# = testfloat# - 0.1
   EndIf

Wend

When I cycle through the numbers, everything is fine until testfloat# becomes 0.1 or 0.0. When it should be 0.1, Blitz will skip from 0.2 to 0.0999999, and when it should be 0.0, it turns into a very small exponent just below 0 (-0.0000000745058). These are both close enough to the real value that I don't care if they're handled this way, I was mostly just wondering if a double would have the same results.

Last edited 2011


Yasha(Posted 2011) [#2]
Yes, but the error would be smaller.

The problem: IEEE floating-point numbers are stored as a binary fraction, which unfortunately isn't capable of representing numbers like 0.1 accurately as they happen to be infinitely recurrent in that representation. Operations involving that number will thus actually become operations involving the nearest representable value.

The solution: don't ever write code that relies on floating-point numbers returning an exact result. e.g. Never compare the values of two floats, but instead compare their difference:

Local f1# = 0.1, f2# = (f1 + 0.2) - 0.1

Print (f1 = f2)    ;BAD

Const EPSILON = 0.001
Print Abs(f1 - f2) > EPSILON    ;GOOD


When you want to do things like have a nice printable version of the number, you effectively have to clean it up first. The only real advantage of doubles for this is that the errors might be small enough that if the value is cast back to a float for printing, it will clean itself.

Oh and for the love of all that is good in the world, don't use floats or doubles to store financial values!

Basically... yes, this is arguably a bug, but nothing to do with Blitz. The general assumption is that floats are "good enough" for most high-speed purposes (e.g. 3D graphics - who can see the difference?) and if you really need an accurate value, you can afford to sacrifice speed and use a bignum of some kind instead, or perhaps a fixed-point fractional number (i.e. an int, but move the decimal place by a known amount when it's time to print the value).

Last edited 2011


Rob the Great(Posted 2011) [#3]
Hmm.......This actually explains a lot of some very frustrating problems in the past. I've run into problems where I would be comparing two floats, but I could never get them to be the same in some situations. Many debugging hours later, I eventually resolved this by just giving a little bit of error range, such as (much like your example):
"If number1# <= number#2 + 0.05 And number1# >= number2# - 0.05".
I like the ABS comparison, though. It looks cleaner.


Yasha(Posted 2011) [#4]
Learned some interesting stuff myself. In wondering why this Doubles DLL: http://www.blitzbasic.com/Community/posts.php?topic=94852

...is so large and complicated, I discovered the issue of FPU modes, which I never knew about before. Interesting reading here: http://stereopsis.com/FPU.html

So simple solutions like simply writing a C function to accept a couple of 8-byte objects, do something to them, and return a result, wouldn't actually work. Fascinating.

I would have just used a one-liner function through TCC for this... and likely wouldn't have worked at all! Although as it happens TCC does support the mode switch function too, so it could be made to work. (Not much point in this tangent unless you also have a use for TCC, note.)

Last edited 2011


Rob the Great(Posted 2011) [#5]
Interesting stuff, but most of this is above my knowledge in programming. I kinda-sorta get what's going on, but I'm only vaguely familiar with C based languages. I just never spent the time to learn anything outside of Blitz3D. I even tried reading the MIT book regarding LISP that you provded a while back, and I had a rough time getting through the first ten pages. lol.


xlsior(Posted 2011) [#6]
Realistically, you can never assume that a float is identical in comparisons, they are only good up to a certain number of digits of accuracy.

When comparing floats, you should always compare against a range:

in stead of: if x=0 then...

you should use something like:
if x>= -0.001 and x<= 0.001

to make sure you don't run into the rounding issue.