Floats 'too precise' ?

BlitzMax Forums/BlitzMax Programming/Floats 'too precise' ?

SystemError51(Posted 2013) [#1]
I'm trying to figure out a rather interesting issue. In my project I work with 3D vectors a lot. For certain things to work, I sometimes need to compare a float that's in a file with a float in memory.

In this example, I supply a float of 0.001 for the x-coordinate.

This is what I get:



Where does it get this extra precision from? I used WriteFloat to write the value into a file.

Am I missing something? Or simply doin it rong?


Chapman7(Posted 2013) [#2]
http://www.blitzbasic.com/Community/posts.php?topic=61799

Might try using a Double


ImaginaryHuman(Posted 2013) [#3]
Floats cannot accurately represent every number you throw at them, it has to round them to the nearest representable value based on the bits its can use in the floating point data.

There's not such thing as being too precice, only imprecice.


GfK(Posted 2013) [#4]
Floats are just inherently inaccurate. I don't pretend to fully understand the finer points of floating point maths, but suffice to say you'll hit the same issue in any language - not just BlitzMax.

You *could* use a Double, and while it does give a greater degree of accuracy, even that isn't particularly suitable for a direct comparison between two values, as you can never rely on the two values being absolutely identical (even if they should be).


TomToad(Posted 2013) [#5]
Best way to compare floats is to compare the difference with a tolerance factor. Something like this

local x:float = .000001
if abs(x-.000001) < .0000000001
   Print "x is equel"
Else
   Print "x is not equal"
end if



Floyd(Posted 2013) [#6]
If you are curious about where that 0.00100000005 came from then here's the explanation.


A single precision float is represented internally as a fraction with a denominator which is a power of 2. The value 0.001 is approximated by 8589935 / 2^33. That is about 0.0010000000475, a little bigger than the exact value.

When BlitzMax converts a float to a string, such as when doing Print, it rounds to nine significant digits. That means nine digits starting with the leftmost non-zero.

In this case 10000000475 is rounded to 10000000500, to display 0.00100000005.

The apparently excessive nine digits are needed if the string is ever converted back to a float. If x# is converted to a string, and the string is converted to a float then the original value of x is exactly recovered. It is somewhat surprising that eight digits will not suffice for every possible x.


SystemError51(Posted 2013) [#7]
Wow this is pretty interesting information right there... I didn't know that. I'll try the code above and see how far I get. I'll also try Float and Double - just to see how well it goes or what's more suitable.


Yasha(Posted 2013) [#8]
All the information you could possibly want on this subject and more can be found here: http://www.validlab.com/goldberg/paper.pdf

Given what you're doing, there may be an easier way around this, though - do you need to use floats at all? Floats are useful because they give excellent performance and use minimal memory. But if you've already got the overhead of reading from a file... you might find that there is a solution involving rationals or number strings that lets you compare values without any precision problems. Convert to float only when it's time to load up into the engine proper?


ImaginaryHuman(Posted 2013) [#9]
Use a Long with fixed point math, e.g. shr and shl 32 places for example, for 32 binary bits after the decimal point.


Henri(Posted 2013) [#10]
Hello,

any suggestions how to calculate something like 0,02 + 0,15 and get accurate result which in this case would be 0,17 ? Speed is not an issue.

-Henri


SystemError51(Posted 2013) [#11]
Thanks ImaginaryHuman, I will look into that too.


BlitzSupport(Posted 2013) [#12]
@Henri, Brucey's wrapper for the MAPM arbitrary-precision maths library seems to handle this:

https://code.google.com/p/maxmods/downloads/list

(Scroll down to mapm_1_00_src.zip.)

SuperStrict

Import BaH.MAPM

Print 0.02 + 0.15

Local value1:TMAPM = New TMAPM.Create("0.02")
Local value2:TMAPM = New TMAPM.Create("0.15")

value1 = value1.Add (value2)

Print value1.ToString ()



Henri(Posted 2013) [#13]
Alright, I'll check that, thanks

-Henri


Yan(Posted 2013) [#14]
any suggestions how to calculate something like 0,02 + 0,15 and get accurate result which in this case would be 0,17 ? Speed is not an issue.
If it's for some kind of decimal monetary system, you wouldn't need to use floats at all:

0.02 + 0.15 would become 2 + 15 and you'd do a simple bit of string formatting at display time.


SystemError51(Posted 2013) [#15]
From my end, I solved the problem using Doubles. These don't seem to change anymore, which is what I need. Comparisons between two doubles seem to work out fine.

However, I may consider Yan's suggestion also.