Floats 'too precise' ?
BlitzMax Forums/BlitzMax Programming/Floats 'too precise' ?
| ||
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? |
| ||
http://www.blitzbasic.com/Community/posts.php?topic=61799 Might try using a Double |
| ||
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. |
| ||
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). |
| ||
Best way to compare floats is to compare the difference with a tolerance factor. Something like thislocal x:float = .000001 if abs(x-.000001) < .0000000001 Print "x is equel" Else Print "x is not equal" end if |
| ||
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. |
| ||
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. |
| ||
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? |
| ||
Use a Long with fixed point math, e.g. shr and shl 32 places for example, for 32 binary bits after the decimal point. |
| ||
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 |
| ||
Thanks ImaginaryHuman, I will look into that too. |
| ||
@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 () |
| ||
Alright, I'll check that, thanks -Henri |
| ||
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. |
| ||
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. |