JeanCremers 1 Posted November 23, 2022 (edited) So i thought 0.1 was representable in floating point, obviously not. How do i use IsZero() or SameValue in a situation like this. updownbuttonclick(TObject *Sender, TUDBtnType Button) { value += (Button == btNext ? 0.1 : -0.1); } What i'm doing now is convert value to a string and then back to fp, this eliminates the cumulative rounding error. How to tackle this with one of those math functions? Thanks. Edited November 23, 2022 by JeanCremers Share this post Link to post
David Heffernan 2353 Posted November 23, 2022 (edited) Quote IsZero or SameValue Neither. Spawn of satan, should never have been published. In your case do all the behind the scenes work using integers and then divide by 10 just when you need the actual value. Or use a decimal type like currency. Edited November 23, 2022 by David Heffernan 1 Share this post Link to post
JeanCremers 1 Posted November 23, 2022 1 hour ago, David Heffernan said: Neither. Spawn of satan, should never have been published. In your case do all the behind the scenes work using integers and then divide by 10 just when you need the actual value. Or use a decimal type like currency. 🙂 Thanks. I tried the multiply/divide trick, but not with 10 but with 100000 and ran into similar problems. Converting to a string and back to a float at least seems to work against the cumulative issue. Share this post Link to post
David Heffernan 2353 Posted November 23, 2022 What I propose works and it's better to avoid strings here. Share this post Link to post
JeanCremers 1 Posted November 23, 2022 (edited) Why not use a string? FloatToStr(0.1) always makes "0.1", so it avoids the rounding error. Edited November 23, 2022 by JeanCremers Share this post Link to post
David Heffernan 2353 Posted November 23, 2022 (edited) {$APPTYPE CONSOLE} uses SysUtils; procedure Main; var i: Integer; val: Double; begin val := 0; for i := 0 to 80 do begin Writeln(FloatToStr(val)); val := val + 0.1; end; end; begin try Main; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; Readln; end. Output: 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 4 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 5 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 5.99999999999999 6.09999999999999 6.19999999999999 6.29999999999999 6.39999999999999 6.49999999999999 6.59999999999999 6.69999999999999 6.79999999999999 6.89999999999999 6.99999999999999 7.09999999999999 7.19999999999999 7.29999999999999 7.39999999999999 7.49999999999999 7.59999999999999 7.69999999999999 7.79999999999999 7.89999999999999 7.99999999999999 So yeah, not looking such a good idea now. Why not use integers. You just need to count how many tenths you have. That's the job of an integer. 3 hours ago, JeanCremers said: So i thought 0.1 was representable in floating point, obviously not. Once you recognise this, then you are 90% of the way to realising the right solution. Edited November 23, 2022 by David Heffernan Share this post Link to post
JeanCremers 1 Posted November 23, 2022 I'm doing something like this, for my purposes it works fine. double r = 0.1, inc = 0.1; String S; for (int i = 0; i < 80; i++) { S = FloatToStr(r); r = StrToFloat(S) + inc; ListBox1->Items->Add(S); Share this post Link to post
JeanCremers 1 Posted November 23, 2022 ps, it's not only meant for doing 0.1 , it should also handle other values. Share this post Link to post
Anders Melander 1815 Posted November 23, 2022 3 hours ago, David Heffernan said: Spawn of satan, should never have been published. Why? What's wrong with them? Share this post Link to post
David Heffernan 2353 Posted November 23, 2022 52 minutes ago, Anders Melander said: Why? What's wrong with them? They are invariably used as "solutions" to problems caused by floating point representability issues, but usually are the wrong solution. Share this post Link to post
David Heffernan 2353 Posted November 23, 2022 (edited) 58 minutes ago, JeanCremers said: ps, it's not only meant for doing 0.1 , it should also handle other values. Then you are going to need to work out what your problem really is. Because it hasn't been stated here. If you want to work with numbers that have a finite number of decimal digits, then use a decimal type. It is precisely why such things exist. Edited November 23, 2022 by David Heffernan Share this post Link to post
Anders Melander 1815 Posted November 23, 2022 3 minutes ago, David Heffernan said: They are invariably used as "solutions" to problems caused by floating point representability issues, but usually are the wrong solution. Okay, so there's basically nothing wrong with the functions. Thanks. Share this post Link to post
JeanCremers 1 Posted November 23, 2022 8 minutes ago, David Heffernan said: Then you are going to need to work out what your problem really is. Because it hasn't been stated here. If you want to work with numbers that have a finite number of decimal digits, then use a decimal type. It is precisely why such things exist. I'm ok with my current solution, it does what i need it to do. Share this post Link to post
David Heffernan 2353 Posted November 23, 2022 32 minutes ago, Anders Melander said: Okay, so there's basically nothing wrong with the functions. Right. They do what they are meant to do. It's just that they aren't actually useful for anything. Share this post Link to post
Vandrovnik 215 Posted November 23, 2022 (edited) 54 minutes ago, JeanCremers said: I'm ok with my current solution, it does what i need it to do. If you use type "currency" instead of "double", it would work too and you can avoid most of the conversions between numerical and string types. Currency can keep 4 decimal places (even the 0.1 🙂 ). Edited November 23, 2022 by Vandrovnik Share this post Link to post
Anders Melander 1815 Posted November 23, 2022 31 minutes ago, David Heffernan said: They do what they are meant to do. It's just that they aren't actually useful for anything. So how would you compare floats with tolerance? function BinarySearch(const List: TArray<Double>; Value: Double; Tolerance: Double): integer; begin var Lo := 0; var Hi := High(List); while (Lo ≤ Hi) do begin var Mid := (Lo + Hi) div 2; if SameValue(List[Mid], Value, Tolerance) then exit(Mid); if List[Mid] < Value then Lo := Mid+1 else Hi := Mid-1; end; Result := -1; end; Share this post Link to post
Stefan Glienke 2019 Posted November 23, 2022 24 minutes ago, Anders Melander said: So how would you compare floats with tolerance? Certainly not like that because this will not consider a value next to the potential find that is a closer match - when the list has a different count of elements the result could be different. Share this post Link to post
Attila Kovacs 631 Posted November 23, 2022 3 minutes ago, Stefan Glienke said: Certainly not like that because this will not consider a value next to the potential find that is a closer match - when the list has a different count of elements the result could be different. match, matcher, matchest? The tolerance determines the result, isn't it? 1 Share this post Link to post
JeanCremers 1 Posted November 23, 2022 (edited) 1 hour ago, Vandrovnik said: If you use type "currency" instead of "double", it would work too and you can avoid most of the conversions between numerical and string types. Currency can keep 4 decimal places (even the 0.1 🙂 ). Can't do, i need type double, its for a script language and it has no currency type, but it has double. And this works for my needs. Edited November 23, 2022 by JeanCremers Share this post Link to post
Anders Melander 1815 Posted November 23, 2022 1 hour ago, Stefan Glienke said: Certainly not like that because this will not consider a value next to the potential find that is a closer match - when the list has a different count of elements the result could be different. You're right. I tried to come up with a trivial example - and failed. Anyway, you get the point. Share this post Link to post
Pat Foley 52 Posted November 23, 2022 Whynot? UItext := Format('%f.%f', [MN div 10, MN mod 10]); MN := round(anumber*10); when MN is Integer . _Trunc or _Floor may be available in your Script as well. Share this post Link to post
Stefan Glienke 2019 Posted November 23, 2022 5 hours ago, Attila Kovacs said: match, matcher, matchest? The tolerance determines the result, isn't it? Upper and lower bound - look it up Share this post Link to post
Attila Kovacs 631 Posted November 23, 2022 3 minutes ago, Stefan Glienke said: Upper and lower bound - look it up Ok I thought he cited it from a working lib... @Anders Melander how dare you!?? Share this post Link to post
Anders Melander 1815 Posted November 23, 2022 38 minutes ago, Attila Kovacs said: Ok I thought he cited it from a working lib... @Anders Melander how dare you!?? Well, it's just pseudo code that happens to look a lot like Delphi code 🙂 . In my defense, I think it actually works as I intended; It will return a match within the tolerance but it might not return the closest match. 1 Share this post Link to post
David Heffernan 2353 Posted November 24, 2022 14 hours ago, JeanCremers said: Can't do, i need type double, its for a script language and it has no currency type, but it has double. And this works for my needs. This certainly changes things! Share this post Link to post