Page 1 of 1

Strange result wiht float numbers

Posted: Sun Dec 08, 2024 12:28 pm
by Rudy M
As beginner I'm still testing Purebasic.
I did a test with float numbers.

Created: 6 float numbers with 5 digits after the decimal point.

When look after debug I see float numbers with 14 digits.
OK, first I taught it was a not correct printing of the numbers.

So I did a test, but I see that Purebasic also did calculations with those extra digits.

Is there something I'm doing not correct?

Rudy M

Code: Select all


;Define.d z

Dim y.f(6)
y(1) = 133.11111
y(2) = 133.22222
y(3) = 133.33333
y(4) = 133.44444
y(5) = 133.55555
y(6) = 133.66666


Debug "Float Numbers:"

For x.l =1 To 6 
  Debug y(x)
Next x  

Debug " "
Debug "Test multiplate y(1) x 2 and debug result:"
Debug y(1)
Debug y(1) *2
Debug " "

Debug "Rounded   "

Define.l Digit = 3

For x.l =1 To 6 
  Debug StrF (y(x), Digit)
Next x  

;Result on screen:

;Float Numbers:
;133.11111450195312
;133.22221374511719
;133.33332824707031
;133.44444274902344
;133.55555725097656
;133.66665649414062

;Rounded   
;133.111
;133.222
;133.333
;133.444
;133.556
;133.667


Re: Strange result wiht float numbers

Posted: Sun Dec 08, 2024 12:51 pm
by miso
You did everything right. The difference is in the way computers store a float (infinite amount of numbers) in a finite number of bits. There is precision loss, so you have to be extremely careful when using them, because those precision loss can add up quickly. Never compare them like for example

Code: Select all

if x.f=y.f
   ;code
endif
instead

Code: Select all

if abs(x.f-y.f)<errorlimit.f
  ;code
endif
In floating point arithmetics a*(b*c) not always equal (a*b)*c.
You can use double for increased precision, but this still applies to doubles too.

Re: Strange result wiht float numbers

Posted: Sun Dec 08, 2024 1:04 pm
by spikey
Rudy M wrote: Sun Dec 08, 2024 12:28 pm Is there something I'm doing not correct?
No. Floats and doubles are only precise at the binary level and also to a finite resolution dictated by the specification (IEEE 754). This leads to some drift when converting from and too decimal representation unless the fraction you are storing is a discrete binary fraction. There's an introduction to the topic at https://ryanstutorials.net/binary-tutor ... -point.php and more detail in https://en.wikipedia.org/wiki/IEEE_754 if you want to understand the "why".

If you want to store currency values accurately there are a few solutions in the forum, search for "Currency Type", for example:
viewtopic.php?t=30625
viewtopic.php?t=32094

Re: Strange result wiht float numbers

Posted: Sun Dec 08, 2024 1:21 pm
by BarryG
@Rudy: This also happens in Microsoft Visual Basic, so don't assume it's just a PureBasic issue. ;)

Image

Re: Strange result wiht float numbers

Posted: Sun Dec 08, 2024 2:44 pm
by mk-soft
It is not a problem. You have to know (or learn) that as a programmer

Re: Strange result wiht float numbers

Posted: Sun Dec 08, 2024 3:22 pm
by benubi
This basically also shows you why you should always use integers for currencies! This means in practice you should use at least a 64 bit number but they may not be enough in case of hyper-inflation so probably they use larger numbers (128bit, 256 bit), or of variable length in professional and scientific economy programs (and currency/math libraries).

Code: Select all


; Use integer values (quad) to express currency values (most basic way)

Procedure.q GetCurrencyValue(NominalFull.q,NominalCentile)
   CurrencyValue.q = (NominalFull* 100) + NominalCentile
ProcedureReturn CurrencyValue
EndProcedure


Procedure GetCurrencyCentile(Value.q)
  ProcedureReturn Value % 100
EndProcedure

Procedure.s StrCurrency(Value.q)
  Protected result$
  Protected rest 
  If Value>=0
   result$="+"
 EndIf
 rest = value % 100
  result$ = result$ + Str(Value/100) + "," + RSet(Str(Abs(rest)),2,"0")
  ProcedureReturn result$
EndProcedure


Debug StrCurrency(123499)+ " EUR"
Debug StrCurrency(-495)+ " USD"

Debug "LIMIT:"
Debug " " + $7FFFFFFFFFFFFFFF +" quad integer value"
Debug StrCurrency($7FFFFFFFFFFFFFFF)+" CAN$"
Debug FormatNumber($7FFFFFFFFFFFFFFF/100) + "  float/double precision error" ; precision/accuracy is limited

Now you see why you should only use integers for currency calculations. And because of the limitations that also will arise with 64bit quads, the possibility that centiles are too big (some currencies and petrol prices have thousands of a currency unit, or 1/4th and 1/2th kind of sub-units) - all that causes the necessity for a well thought-through concept of a currency data type when you create an application or database, and want to make conversions. Imagine that, there are countries were a loaf of bread may coast millions or billions of currency units, because of hyper-inflation, so this goes into exotic long numbers with variable length maybe... IMO that's pretty interesting. I wonder what they use in MS for the data type I believe it's a 128 bit integer and they use millis, plus there must be an indicator word or long for the currency type (euro, dollar etc.), the resulting structure or data-type would be at least around 18-20 bytes (compared to the 8 a quad would take). But one could imagine very dynamic types with variable sizes. For example the first byte may indicate how many bytes are needed to express the currency units, and the second how many bytes for the millis or centis (and what kind they are either millis or centies etc.)

Code: Select all

Structure MySuperCurrencyStructure
  CurrencyID.l ; Could be a readable FourCC or three letters with null termination i.e. "EUR" or "USD"
  Value.q[2] ; 128 bit big number (must be used with a special math library)
  ; perhaps in tenthousands of a unit 0..9999 . One cent = 100 tenthousands :)
EndStructure
But this is only an idea. The fractions of the unit may be also very important for example when the main unit increases a lot in value (e.g. through deflation), and fractions of it become more of interest and fine granulated, as is the case with bitcoin or so (?). So finding the perfect technique for currency management for ANY case from scratch, if it's of importance to you, might become very tricky and adventurous :)

Re: Strange result wiht float numbers

Posted: Mon Dec 09, 2024 10:00 am
by Rudy M
Thank you all for sharing your knowledge and experience, and for the demo listing with the "quad" idea.

It is almost unbelievable that I did not encounter this problem in my programs in the past.
In MS basic PDS + ASM routines for DBFfile access I made programs for making paint, glue and varnish. For mixing paint colors the same.
But there I did store the numbers in fixed length string (to store in DBF fields).
The only thing I can assume is that this last one filtered out the pesky (*) number junk. (*) translated with Google translate...

But now back to Purebasic:
To go further on the above quad idea....
Do you think it is a good idea to try to circumvent this problem for my beer brewing program (as an experiment) by storing numerical data (weights and volumes) in quad?
Perform all calculations with quad and after calculations "round off" where necessary (with PB “round” and/or a self-made procedure...).

For an amateur brewer (and for my installation) I'm going the following limits:
The smallest amount to be added = 1 gram.
Maximum limit of weight in kg could be: 1000kg
(My vessel is only 50L but I have friends who make beer with 200L .... so they can also use my program)
1000kg = 1000000g so I should control after data input (or calculation) for this max. limit.

For printing on the screen (or on paper) the program itself can determine in which unit the recipe should be printed on the screen or on paper.
0 to 999 grams in grams else (>=1000 grams) in kilogram format.

Rudy M

P.S.: I make my reply's in Dutch and translate them to Englisch with Google translate

Re: Strange result wiht float numbers

Posted: Mon Dec 09, 2024 10:22 am
by NicTheQuick
With doubles you have a precision of 15 digits, with floats the precision is 7 digits. For a bit of beer brewery this is enough. Don't go the way and misuse quads for that purpose.

Re: Strange result wiht float numbers

Posted: Mon Dec 09, 2024 10:27 am
by Rudy M
@NicTheQuick: Thanks!

I 'll follow Your tip.
Before I do some tests with (storing, calculating (p.e. rule of 3... for other volume...) and then printout the result.

Rudy M

Re: Strange result wiht float numbers

Posted: Mon Dec 09, 2024 10:34 am
by miso
Hello Rudy M!

To be honest, I don't have even basic understanding about beer brewing, but I think even a rounded float to integer ( representing the smallest value as gram) should do the trick.

Currencies and absolutely precise scientific calculations need different approach, but I don't think you would ruin your beer because of floats.
( that's just personal opinion, I don't take responsibilities for ruined beers ;) )

Re: Strange result wiht float numbers

Posted: Mon Dec 09, 2024 2:01 pm
by blueb
Here's a simple procedure that may help.
I store these things in the template folder of the IDE :mrgreen:

Code: Select all

Procedure.s StrRounder(X.d, nPlaces.i=2) 
     ; A helper because floats and doubles cannot store every number exactly as written... by skywalk (Oct 7, 2016)
     Protected.i Scale = Pow(10, nPlaces)
     ProcedureReturn StrD(Round(X*Scale, #PB_Round_Nearest)/Scale, nPlaces)
EndProcedure

; A Float/Double Number
Value.d = 21.505
Debug "How the system actually sees the value!"
Debug Value
Debug "----------------"
Debug "Result: " + StrD(Value,  2) ; PureBasic method
Debug "----------------"
Debug "Result: " + StrRounder(Value, 2) ; above procedure


Re: Strange result wiht float numbers

Posted: Mon Dec 09, 2024 2:13 pm
by Rudy M
@Miso:

I make always a (in Basic Pds a sub or function), in PB a procedure... "Rule of three".
This is practical in case you have too little of some raw material.
Are You want to make a recipe a little smaller or bigger, etc.
And with high accuracy of the numerical variables you get no or few deviations from the original recipe,
even when going a few times up or down in quantity.

But You are right, for beer that little bit of difference doesn't matter.
it's a bit of a professional deformation of mine when I used to make paint and color recipes....
(Old color example. in a 5L white paint paintcan somethimes 0.1 to 0.3 g of blue, black or omber pigment to obtain the correct tint).

Thanks for your input!

Rudy M