Jump to content
JeanCremers

IsZero or SameValue

Recommended Posts

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 by JeanCremers

Share this post


Link to post
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 by David Heffernan
  • Haha 1

Share this post


Link to post
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

Why not use a string?

FloatToStr(0.1) always makes "0.1", so it avoids the rounding error.

Edited by JeanCremers

Share this post


Link to post
{$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 by David Heffernan

Share this post


Link to post

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);

2022-11-23_15-36.png

Share this post


Link to post
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
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 by David Heffernan

Share this post


Link to post
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
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
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
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 by Vandrovnik

Share this post


Link to post
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
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
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?

  • Like 1

Share this post


Link to post
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 by JeanCremers

Share this post


Link to post
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

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
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
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.

  • Like 1

Share this post


Link to post
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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×