Author Topic: How do you deal with floating point rounding errors ?  (Read 13102 times)

0 Members and 1 Guest are viewing this topic.

Offline CharlieJV

  • Newbie
  • Posts: 89
    • View Profile
Re: How do you deal with floating point rounding errors ?
« Reply #15 on: April 02, 2022, 01:36:00 pm »
Personally, here's the general method I use when I have to deal with this type of thing:

Code: QB64: [Select]
  1. For i = -1 To 1 Step 0.1
  2. ...
(snip!)

I don't know if I can ever again trust a loop that involves incrementing by decimal values.

Maybe a knee-jerk reaction, but I find myself wondering, looking at any kind of loop like that, if there is unintentional short-changing of the loop (or an opposite "over-loop"?) by an iteration.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: How do you deal with floating point rounding errors ?
« Reply #16 on: April 02, 2022, 01:50:20 pm »
I don't know if I can ever again trust a loop that involves incrementing by decimal values.

Maybe a knee-jerk reaction, but I find myself wondering, looking at any kind of loop like that, if there is unintentional short-changing of the loop (or an opposite "over-loop"?) by an iteration.

It's always a possibility.

FOR i = -1 TO 1 STEP 0.1

NEXT

In the above, even if we add a line that sets i to being exactly 0.9, we can't be certain that the increment won't make it's value 1.00000001 -- which is greater than 1 and will xit the loop one step early!

The only way to be certain we get that final loop is by adding a tolerance level into the code for floating point imperfections:

FOR i = -1 TO 1.01 STEP 0.1

NEXT

The end point is now great enough to absorb the error, but not large enough to allow an extra increment to pass.



I still say the absolute best solution, when possible, is to just avoid the floating point imperfections entirely.

FOR i = -10 TO 10 'use integers when possible
  iDec = i /10 'only convert to the floating point value when necessary
NEXT

It's why the banks track how many PENNIES are in your account, and not how many dollars.  The results are displayed as dollars, but all the calculations are in pennies.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline CharlieJV

  • Newbie
  • Posts: 89
    • View Profile
Re: How do you deal with floating point rounding errors ?
« Reply #17 on: April 02, 2022, 02:45:33 pm »
(SNIP!)
I still say the absolute best solution, when possible, is to just avoid the floating point imperfections entirely.

FOR i = -10 TO 10 'use integers when possible
  iDec = i /10 'only convert to the floating point value when necessary
NEXT

It's why the banks track how many PENNIES are in your account, and not how many dollars.  The results are displayed as dollars, but all the calculations are in pennies.

Yeah, I do believe I am firmly in that camp.

I might just adopt a standard variable and approach for that kind of thing.

like:

Code: QB64: [Select]
  1. fpa = 10 ' floating-point adjustment
  2. FOR i = -1*fpa TO 1*fpa step 0.1*fpa
  3.   print = i/fpa
  4.  
  5. fpa = 100 ' floating-point adjustment
  6. FOR i = -0.1*fpa TO 0.1*fpa step 0.01*fpa
  7.   print i /fpa
  8.  

Maybe.  Time, mood, and pudding o' proof will tell.

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: How do you deal with floating point rounding errors ?
« Reply #18 on: April 02, 2022, 02:59:21 pm »
CharlieJV
maybe use while loops instead

Offline CharlieJV

  • Newbie
  • Posts: 89
    • View Profile
Re: How do you deal with floating point rounding errors ?
« Reply #19 on: April 02, 2022, 04:33:32 pm »
CharlieJV
maybe use while loops instead

You've lost me there.  How does a while help?

(Aside and unrelated: I don't really see while loops and for loops as always interchangeable, each to me better than the other depending on the job at hand, often times just for the sake of readability.)

Trying to figure out what you mean, but I'm sure I'm way off:
Code: QB64: [Select]
  1. fpa = 10 ' floating-point adjustment
  2. i = -1*fpa
  3. WHILE i <= TO 1*fpa
  4.   print = i/fpa
  5.   i = i + 0.1*fpa
  6.  

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: How do you deal with floating point rounding errors ?
« Reply #20 on: April 02, 2022, 04:41:22 pm »
@CharlieJV  you might be in habit of thinking of i as an incremented (by 1) integer index (like for arrays).

When i is an integer it will do the last loop
Code: QB64: [Select]
  1. for i = 1 to 10
  2.  print i

jack is suggesting a While loop I think to try an get that last loop for i when it isn't an incremented (by 1) integer index but I had same idea tried it and still i a single type + .1 collects gabage and passes exactly 1.000 just like in For loop!



Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: How do you deal with floating point rounding errors ?
« Reply #21 on: April 02, 2022, 05:57:40 pm »
@SMcNeill

"i = Int(i * 10 + .5) / 10"

That's what I used some 35 years ago to handle accounting in my Quick Basic office software. Never any errors as far as my applications were concerned for addition, subtraction, multiplication, and division.

Great minds think a like, and you and I come up with the same ideas, once in awhile, too! (Left myself wide open on that one.)

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline CharlieJV

  • Newbie
  • Posts: 89
    • View Profile
Re: How do you deal with floating point rounding errors ?
« Reply #22 on: April 02, 2022, 07:02:44 pm »
@CharlieJV  you might be in habit of thinking of i as an incremented (by 1) integer index (like for arrays).

No, I am not thinking that.  When I do a For i = 1 to 5, I expect 1,2,3,4,5 to happen.


jack is suggesting a While loop I think to try an get that last loop for i when it isn't an incremented (by 1) integer index but I had same idea tried it and still i a single type + .1 collects gabage and passes exactly 1.000 just like in For loop!

As per the example you had given with .1, my solution to the problem is to multiply everything by 10 so that we are no longer dealing with floating point numbers, and then divide by ten when we want to show the floating point values.

And that works great.


I'm very confused because I don't understand what point either you or Jack are trying to make.  With the samples I gave, I don't see what value a while-wend would provide over a for-next.



Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: How do you deal with floating point rounding errors ?
« Reply #23 on: April 02, 2022, 07:31:03 pm »
While... Wend or Do...Loop or GOTO whatever...  *WON'T* make any difference.

The problem is -- and say it with me when I repeat it once more guys -- IT'S IMPOSSIBLE TO PERFECTLY REPRESENT 1/10 IN BINARY FORMAT!!  It simply can't be done!

1/10 is a flawed representation in binary, just as it's impossible to perfect represent 1/3 in decimal.  All you can do is give the closest working estimation for the value -- which is basically a "close enough" figure for most applications.   

The initial value of 1/10 is flawed, by its very nature in binary.  Adding it up repeatedly, in ANY sort of situation -- loop or not -- simply increases the magnitude of that incremental flaw.

Charlie has a good, working solution: Convert to integer values and do away with the floating point inaccuracies completely.  Minimizing usage of floating point variables minimizes the loss of precision which is inherent in their very nature.

It's just the inherent nature of the math base at work.

https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: How do you deal with floating point rounding errors ?
« Reply #24 on: April 02, 2022, 09:14:04 pm »
Quote
@CharlieJV  you might be in habit of thinking of i as an incremented (by 1) integer index (like for arrays).

No, I am not thinking that.  When I do a For i = 1 to 5, I expect 1,2,3,4,5 to happen.

LOL, I thought April Fools day was yesterday!

And I say this:
Quote
jack is suggesting a While loop I think to try an get that last loop for i when it isn't an incremented (by 1) integer index but I had same idea tried it and still i a single type + .1 collects gabage and passes exactly 1.000 just like in For loop!
Translation While doesn't work either.

And both you and Steve repeat there is no advantage to While, so yeah! we all agree.
« Last Edit: April 02, 2022, 09:21:04 pm by bplus »

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: How do you deal with floating point rounding errors ?
« Reply #25 on: April 02, 2022, 09:39:26 pm »
gcc runtime library has decimal arithmetic functions https://gcc.gnu.org/onlinedocs/gccint/Decimal-float-library-routines.html so it may be possible to use them in QB64
tried it, but unfortunately QB64 passes the arguments by reference and they need to be by value
since the functions are inline and there's no library I can't use the byval keyword
Code: QB64: [Select]
  1. declare Function __bid_adddd3~&& (a~&&, b~&&)
  2. declare Function __bid_extenddfdd~&& (a#)
  3. declare Function __bid_truncdddf# (a~&&)
  4.  
  5. Dim As Double dx, dy
  6.  
  7. dx = 3.141592653589793
  8. x = __bid_extenddfdd~&&(dx)
  9. y = bid_add(x, x)
  10. dy = __bid_truncdddf#(y)
  11.  

Offline CharlieJV

  • Newbie
  • Posts: 89
    • View Profile
Re: How do you deal with floating point rounding errors ?
« Reply #26 on: April 02, 2022, 09:55:26 pm »
LOL, I thought April Fools day was yesterday!
(SNIP!)

Pff, I don't need a special day to be fooled something silly.  No pride, no fear: bring it on baby!

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: How do you deal with floating point rounding errors ?
« Reply #27 on: April 02, 2022, 10:05:17 pm »
Hey Steve I was thinking - uh oh, get ready...

...and I know you're infinitely busy already, all thoughts and prayers going your way. That said, can I humbly encourage your QB64 Bible project to hit the issue of floating point math next? Even if it wasn't the next chapter you were going to write, we need something final on this question once and for all. It comes up way too often in the forums. The proper write-up should minimize out-of-house references, and be something written entirely by us, for us -  and you're the guy to do it Steve. The challenge is to write it out *so good* that it answers every question that ever came up about floating point math in qb64.exe, and also anticipates every future question that may arise. Do whatever you want to do, but make this section stand perfectly alone, and write it soon. No references to other chapters, just a stand alone gospel on QB64's float.

This might make the overall project feel less daunting. Do chapters by demand. Do float while it's fresh. Invest the 10,000 keystrokes and you'll save us all 100,000. Pretty Virginia Please.
You're not done when it works, you're done when it's right.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: How do you deal with floating point rounding errors ?
« Reply #28 on: April 03, 2022, 12:05:22 am »
Hey Steve I was thinking - uh oh, get ready...

...and I know you're infinitely busy already, all thoughts and prayers going your way. That said, can I humbly encourage your QB64 Bible project to hit the issue of floating point math next? Even if it wasn't the next chapter you were going to write, we need something final on this question once and for all. It comes up way too often in the forums. The proper write-up should minimize out-of-house references, and be something written entirely by us, for us -  and you're the guy to do it Steve. The challenge is to write it out *so good* that it answers every question that ever came up about floating point math in qb64.exe, and also anticipates every future question that may arise. Do whatever you want to do, but make this section stand perfectly alone, and write it soon. No references to other chapters, just a stand alone gospel on QB64's float.

This might make the overall project feel less daunting. Do chapters by demand. Do float while it's fresh. Invest the 10,000 keystrokes and you'll save us all 100,000. Pretty Virginia Please.

The problem with floating point stuff is that *even I* -- as unbelievable as that sounds -- am still learning more about how it behaves and what its quirks are all the time.

Here's a quirk I just picked up on a little while ago that made me just close my QB64 window and give up on things for a while:

Code: QB64: [Select]
  1. i = -1.9
  2. x## = i
  3. Print i, x##

i is a single, and it's value isi -1.9.  x## is a float -- much more precision to work with than a trifling single, so the value should be the same easily.  Right??

  [ You are not allowed to view this attachment ]  

WRONG!!

By passing a SINGLE into a SUB/FUNCTION that looks for a _FLOAT, the value can change just because the variable type used to store that value changed!!

Who would've ever thunk that??  Especially since we're going from low precision floats to high precision floats.  I could almost understand if a float held a value that a single couldn't, but in this case, I'm just blown away by the value change.

Floating Point wierdness....

Can *anyone* ever really write a end-all testament to describe all the oddness that it can perform??
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: How do you deal with floating point rounding errors ?
« Reply #29 on: April 03, 2022, 01:33:33 am »
I know that the world of floating point is wild, but I'm not so taken aback by the example above, especially from a scientific notation point of view. As far as significant figures go, the x## number is just as accurate as the single version, to the precision of single, and that's all we can ask of it. It's like a conservation of information thing. There are lots of ways to justify it this point - something that helps is the single-precision number, print all of the implied zero that follow the last digit. Where ever those zeros end, that's where your x## version's digits start not mattering.

Maybe I should refine my request above. I'm not saying to write something that documents all possible behavior of float, or even to predict the stray digits that occur after the last significant one. What we should do is teach a zen that avoids these questions outright. Don't mess with edge cases, be careful with conversions, don't believe every decimal you see. It's garbage in, garbage out, and only up to the number if significant figures.

It's like calculating the area of something. If you decide some rectangle is 37.23 inches across, and 34.57 inches wide, the product of those numbers is 1287.0411. But ya know what? The ".0411" is complete bullshit if you want to interpret that answer as the area. Each input number had 4 digits, and the area can only be precise to 4 digits, so it's just 1287, no remainder. Anyway, that's same ghost haunting the example you laid out.
You're not done when it works, you're done when it's right.