Author Topic: Floating point math - a banality (sometimes sometimes doubts come back...)  (Read 7057 times)

0 Members and 1 Guest are viewing this topic.

Offline krovit

  • Forum Regular
  • Posts: 179
    • View Profile
Hello everyone, it is always a pleasure to read you and know that we are still here

My question:

The result is always 1.5
It is I who get confused and do something wrong or there is something wrong??

How can i handle decimals? 0.4999999 is not equal to 0.5.

After so many years of QB45 and QB64 it's a question I shouldn't even ask...
I know... it is one of the first and simplest problems faced when writing some code, but today I have doubts...

Thank you



Code: QB64: [Select]
  1.  
  2. ns=1.4999999999999999
  3. ? ns
  4. ? str$(ns)
  5.  
  6. nd=1.4999999999999999
  7. ? nd
  8. ? str$(nd)
  9.  
  10. nf=1.4999999999999999
  11. ? nf
  12. ? str$(nf)
  13.  
  14.  




« Last Edit: August 08, 2020, 09:14:54 pm by odin »
Nothing is easy, especially when it appears simple (and nothing could be as dangerous as trying to do good to others)

Offline RhoSigma

  • QB64 Developer
  • Forum Resident
  • Posts: 565
    • View Profile
Re: a banality (sometimes sometimes doubts come back...)
« Reply #1 on: August 08, 2020, 05:57:29 am »
I usually avoid floating point math wherever and whenever possible. Eg. when calculating currency, then I do the calculations always in Cents, not in Dollars or Euros or whatever. I simply expand all INPUT values from the user with factor 100 and make internal calulations, only for display I convert it back using:

Code: QB64: [Select]
  1. PRINT LEFT$(STR$(cents&), LEN(STR$(cents&)) - 2); "."; RIGHT$(STR$(cents&), 2)

Same principle works for any other "all day" uses, you just need the right factor. Of course, if you calulate atronomic values, then you'll probably overflow even the biggest integer variable type using this method.

Another positive side effect, integer math is much faster than floating point math on every computer.
« Last Edit: August 08, 2020, 08:19:13 am by RhoSigma »
My Projects:   https://qb64forum.alephc.xyz/index.php?topic=809
GuiTools - A graphic UI framework (can do multiple UI forms/windows in one program)
Libraries - ImageProcess, StringBuffers (virt. files), MD5/SHA2-Hash, LZW etc.
Bonus - Blankers, QB64/Notepad++ setup pack

Offline krovit

  • Forum Regular
  • Posts: 179
    • View Profile
Re: a banality (sometimes sometimes doubts come back...)
« Reply #2 on: August 08, 2020, 06:10:59 am »
Sorry... scrolling through the posts I saw of jack asking a question about _FLOAT accuracy on July 28th (I thought I was the only one still dealing with these little problems).
I saw that it used var## variables (which I did not know ... they had escaped me ... but how long have they existed?? a long time, I think, but I hadn't considered it)

However, excuse me, I still don't quite understand...
why if I declare the variable _FLOAT it doesn't work and if I use it directly with ## does it work? why is there a limit of 14 digits after the decimal point?


« Last Edit: August 08, 2020, 06:43:38 am by krovit »
Nothing is easy, especially when it appears simple (and nothing could be as dangerous as trying to do good to others)

Offline krovit

  • Forum Regular
  • Posts: 179
    • View Profile
Re: a banality (sometimes sometimes doubts come back...)
« Reply #3 on: August 08, 2020, 06:17:28 am »
1.4999999999999 = 1.4999999999999
1.49999999999999 = 1.5

with 14 digits after the comma everything works. with 15 and more rounds alone anyway

Nothing is easy, especially when it appears simple (and nothing could be as dangerous as trying to do good to others)

Offline krovit

  • Forum Regular
  • Posts: 179
    • View Profile
Re: a banality (sometimes sometimes doubts come back...)
« Reply #4 on: August 08, 2020, 06:21:41 am »
I usually avoid floating point math wherever and whenever possible. Eg. when calculating currency, then I do the calculations always in Cents, not in Dollars or Euros or whatever. I simply expand all INPUT values from the user with factor 100 and make internal calulations, only for display I convert it back using:

Code: QB64: [Select]
  1. PRINT LEFT$(STR$(cents&), 1); "."; RIGHT$(STR$(cents&), 2)

Same principle works for any other "all day" uses, you just need the right factor. Of course, if you calulate atronomic values, then you'll probably overflow even the biggest integer variable type using this method.

Another positive side effect, integer math is much faster than floating point math on every computer.

Thank you for your contribution, well, you... I, too, usually... maybe that's why I hadn't noticed this "problem"." But now I put it on because at some point the multiplication and division products in any case have numbers after the viorg that need to be evaluated

Nothing is easy, especially when it appears simple (and nothing could be as dangerous as trying to do good to others)

Offline krovit

  • Forum Regular
  • Posts: 179
    • View Profile
Re: a banality (sometimes sometimes doubts come back...)
« Reply #5 on: August 08, 2020, 07:04:44 am »
Sorry I still don't understand...

Another example width _FLOAT variable:

1234567.49999999 = 1234567.49999999
1234567.499999999 = 1234567.5

9 digits after the comma (not 15)


Nothing is easy, especially when it appears simple (and nothing could be as dangerous as trying to do good to others)

Offline Richard Frost

  • Seasoned Forum Regular
  • Posts: 316
  • Needle nardle noo. - Peter Sellers
    • View Profile
Re: a banality (sometimes sometimes doubts come back...)
« Reply #6 on: August 08, 2020, 01:08:49 pm »
http://www.qb64.org/wiki/Variable_Types

It says ## is the same as _FLOAT.

I look forward to the _BITCOIN type, which will make us all billionaires.   Likely this variable type
will only accept the results of divisions by zero. 
It works better if you plug it in.

Offline krovit

  • Forum Regular
  • Posts: 179
    • View Profile
Re: a banality (sometimes sometimes doubts come back...)
« Reply #7 on: August 08, 2020, 04:13:06 pm »
http://www.qb64.org/wiki/Variable_Types

It says ## is the same as _FLOAT.

I look forward to the _BITCOIN type, which will make us all billionaires.   Likely this variable type
will only accept the results of divisions by zero. 


The first out of tune note in many years...

Yes ... I always consult the manual (for decades, I was not born yesterday) and I also search among the answers of other users. I know that certain issues are necessarily trivial but I had never noticed this behavior of the variables. I mainly dedicated myself to data management and interfaces for their entry, and little to calculation. Now, coincidentally, I need some calculations and this thing comes up.

I insist: 1.49999... is different from 1.5 and I would like to decide when to round and how (I know how to do it)
In single precision I could understand. I don't understand becauset rounds itself into _FLOAT and _DOUBLE

I'm certainly missing something extremely simple because I won't be the first to notice this.
Can someone please tell me where am I wrong?

Code: QB64: [Select]
  1. a#=1234567.49999999
  2. ? a#
  3.  
  4. a#=1234567.499999999
  5. ? a#
  6.  
  7. a##=1234567.49999999
  8. ? a##
  9.  
  10. a##=1234567.499999999
  11. ? a##
  12.  
  13. ? STR$(a##)
  14.  
  15.  

If the system rounds by itself I can't do it and it creates some problems with the results



I am asking the question in another way: I need to treat the number 1.4999999999999999999
not like 1.5 but just for what it is 1.4 and a long series of "9"
I thought I had to do it with double precision numbers but obviously I was wrong.

Any suggestions?






« Last Edit: August 08, 2020, 04:45:42 pm by krovit »
Nothing is easy, especially when it appears simple (and nothing could be as dangerous as trying to do good to others)

FellippeHeitor

  • Guest
Re: a banality (sometimes sometimes doubts come back...)
« Reply #8 on: August 08, 2020, 04:48:52 pm »
https://floating-point-gui.de/

Mandatory read, has this been posted in this thread yet?

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: a banality (sometimes sometimes doubts come back...)
« Reply #9 on: August 08, 2020, 08:20:05 pm »
Sounds like you need to write your own string math routines so you can calculate values to whatever precision you desire.  Floating-point values terminate after a set point, depending on OS, math processors, and other factors, and if you’re not getting the level of precision you need, you’ll need to work around the issue with string math, or double-sided integer math (one integer for the left side of the decimal point, another decimal for the right side of the decimal point).
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline krovit

  • Forum Regular
  • Posts: 179
    • View Profile
https://floating-point-gui.de/

Mandatory read, has this been posted in this thread yet?

thanks, I did not know this site. it is a wealth of information
Nothing is easy, especially when it appears simple (and nothing could be as dangerous as trying to do good to others)

Offline krovit

  • Forum Regular
  • Posts: 179
    • View Profile
Sounds like you need to write your own string math routines so you can calculate values to whatever precision you desire.  Floating-point values terminate after a set point, depending on OS, math processors, and other factors, and if you’re not getting the level of precision you need, you’ll need to work around the issue with string math, or double-sided integer math (one integer for the left side of the decimal point, another decimal for the right side of the decimal point).

Thank you, SMcNeill, If I understood your suggestion, yes, in fact I thought I would get around the problem by using strings. I wanted to deal with the whole part and the part after the comma, it wouldn't have been difficult, but the str$(x) function provides the string of the number already rounded without it being able to do anything to prevent it!

It all seems strange to me! it is not possible that I woke up this morning with a problem that I thought I had solved decades ago and that many have faced and overcome before me ...
my question also seems so stupid to me that I apologize first ...

Nothing is easy, especially when it appears simple (and nothing could be as dangerous as trying to do good to others)

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
You’re not looking for STR$(x).  You need to work with X$ directly.

X$ = “12345678901234567890123456789012345678901234567890”
Y$ = “98765432109876543210987654321098765432109876543210”
Z$ = X$ + Y$

What you want is a math routine to basically do the above, which since our strings are the same size, is fairly simple.  In this case, you just need a DO..LOOP to go from right to left, and add the current positions and any carry-over.

0 + 0 = 0
9 + 1 = 10, so second digit is 0, carry the 1 From the ten’s position.
8 + 2 + 1(the carry over) = 11.  Third digit is 1, carry the 1 From the ten’s position.
....continue until you go through the whole string.

When dealing with decimals, it’s the same process; you just convert both string values to the same number of decimal places.   0.0100000 + 0.987654

You’ll need to write routines for string addition, multiplication, subtraction, division, ect as needed, and then you can set your own precision limits to whatever you desire.  Just be warned: it’ll be much slower than binary math.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline krovit

  • Forum Regular
  • Posts: 179
    • View Profile
Thank you, SMcNeill, If I understood your suggestion, yes, in fact I thought I would get around the problem by using strings. I wanted to deal with the whole part and the part after the comma, it wouldn't have been difficult, but the str$(x) function provides the string of the number already rounded without it being able to do anything to prevent it!

It all seems strange to me! it is not possible that I woke up this morning with a problem that I thought I had solved decades ago and that many have faced and overcome before me ...
my question also seems so stupid to me that I apologize first ...




Sometimes I am speechless in front of the answers provided by the functions and the software... then I understand because I do not have a graduated in mathematics or in computer science (and sMcNeill turns the knife into the wound) ;) ... then I review the problems very well illustrated in the site suggested by Fellippe... and then I give up :(

I'm just looking for a simple way to round up as I would like
a# = 1 + .49999999999999
? a#  -->  1.49999999999999

a# = 1 + .499999999999999
? a#  -->  1.5


But it seems that a simple solution does not exist

Anyway thanks for your patience and for the answers (I've always said that this is a forum with tough and prepared people but kind and polite people)

« Last Edit: August 09, 2020, 12:24:36 pm by krovit »
Nothing is easy, especially when it appears simple (and nothing could be as dangerous as trying to do good to others)

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Strange! For some odd reason this doesn't work for x1# = .55555555555555
Code: QB64: [Select]
  1. x1# = 10051.555555555555555555555555555 'Great!
  2. x1# = .3333333333333 'Great!
  3. x1# = .888888888888 'Great
  4. x1# = 5.55555555555 ' Fabulous!
  5. x1# = .444444444444 ' Marvelous!
  6. x1# = 73.737373737373 'Perfecto!
  7. x1# = .666666666666666 'good again!
  8. x1# = .54545454545 'Fine
  9. x1# = .49999999999 'OK was expecting 0's but alright
  10. x1# = .4555555 ' just lovely
  11. x1# = .5 'fine too though expecting 0's
  12. x1# = .55 'OK
  13. x1# = .655 'OK
  14. x1# = .6555 'OK
  15. x1# = .65555 'ok
  16. x1# = .655555 'ok
  17.  
  18. 'x1# = .555 'here it starts! 5 places good 2 places and more decimals sneek in
  19. 'x1# = .5555 'craps again
  20.  
  21. 'x1# = .5555555 'WTH where are the decimals coming from?
  22. 'comment the above line and try others
  23.  
  24. PRINT "Rounded to 5 places: "; RoundDblX2DP$(x1#, 5)
  25. PRINT "Rounded to 2 places: "; RoundDblX2DP$(x1#, 2)
  26. PRINT "Rounded to 0 places: "; RoundDblX2DP$(x1#, 0)
  27. PRINT "Rounded to Tens: "; RoundDblX2DP$(x1#, -1) 'just out of curiousity is this 0  in the 1's place ? yes!
  28. PRINT "Rounded to 100's: "; RoundDblX2DP$(x1#, -2)
  29.  
  30. FUNCTION RoundDblX2DP$ (x AS DOUBLE, DP AS INTEGER)
  31.     ' OK x <> .555...   .55 OK
  32.     'for DP = number of decimal places
  33.     DIM test$, p AS INTEGER, r AS DOUBLE
  34.     r = 5 * 10 ^ (-1 * (DP + 1))
  35.     'PRINT r      'that's it!
  36.     test$ = _TRIM$(STR$(INT((x + r) * 10 ^ DP) / 10 ^ DP))
  37.     p = INSTR(test$, ".")
  38.     IF p = 0 AND DP <> 0 THEN test$ = test$ + "." + STRING$(DP, "0")
  39.     IF p AND DP = 0 THEN test$ = LEFT$(test$, LEN(test$) - 1)
  40.     RoundDblX2DP$ = test$
  41.  
  42.  
  43.  

EDIT: update after more investigation with .55...

One might begin to suspect there was a bug with .5 because this does not behave as expected.
« Last Edit: August 09, 2020, 02:02:12 pm by bplus »