Recent

Author Topic: A computational problem  (Read 1672 times)

lazpas

  • Jr. Member
  • **
  • Posts: 74
A computational problem
« on: October 02, 2022, 03:26:33 am »
44950-7693*4.77=?
The code looks like this:

Code: Pascal  [Select][+][-]
  1. ......
  2. parser := TFPExpressionParser.Create(nil);
  3.   try
  4.     parser.BuiltIns := [bcMath];
  5.     parser.Expression := '44950-7693*4.77';
  6.     parserResult := parser.Evaluate;
  7.     Caption:= FloatToStr(parserResult.ResFloat);
  8.   finally
  9.     parser.Free;
  10.   end;
  11.  

The result is:8254.39000000001

The correct result is:8254.39

Is there a bug in my code?

Thanks

Bogen85

  • Hero Member
  • *****
  • Posts: 595
Re: A computational problem
« Reply #1 on: October 02, 2022, 04:20:20 am »
44950-7693*4.77=?
...

The result is:8254.39000000001

The correct result is:8254.39

Is there a bug in my code?

Thanks


No. Your code is fine. That is normal for binary to decimal conversion. You can't get an exact representation of the floating point number when you convert it to decimal due to base 10 not being a power of 2.
You need to display it with less precision then trim trailing zeros to get something that is more reasonable.
« Last Edit: October 02, 2022, 04:23:11 am by Bogen85 »

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: A computational problem
« Reply #2 on: October 02, 2022, 09:04:59 am »
Or rescale the numbers before the calculation and handle them as integers. 64 bits should be enough to handle most financial calculations for at least a few more months :-/

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

jcmontherock

  • Full Member
  • ***
  • Posts: 234
Re: A computational problem
« Reply #3 on: October 02, 2022, 09:19:19 am »
What about parenthesis in your expression ?
Windows 11 UTF8-64 - Lazarus 3.2-64 - FPC 3.2.2

Bogen85

  • Hero Member
  • *****
  • Posts: 595
Re: A computational problem
« Reply #4 on: October 02, 2022, 09:40:50 am »
What about parenthesis in your expression ?

The result was off by 0.0000000001 according to @lazpas.

That is a normal floating point to decimal conversion issue.

Adding parenthesis around the 7693*4.77 is not going to make floating point to decimal conversion issues magically disappear.  :D

This problem is not limited to Pascal, specifically Free Pascal in this example. You would get the same results with C/C++/python/etc if you don't limit the precision on the displayed output.

Bogen85

  • Hero Member
  • *****
  • Posts: 595
Re: A computational problem
« Reply #5 on: October 02, 2022, 10:16:10 am »
44950-7693*4.77=?
...
The result is:8254.39000000001

The correct result is:8254.39

Is there a bug in my code?

Or just use sysutils.format. It will correct the display issues for you.
https://www.freepascal.org/docs-html/rtl/sysutils/format.html

Code: Pascal  [Select][+][-]
  1. {$mode delphi}
  2.  
  3. program float_precision;
  4.  
  5. uses
  6.   fpexprpars,
  7.   sysutils;
  8.  
  9.   procedure primary (const parser: TFPExpressionParser);
  10.  
  11.     procedure do_sample (const tag, fmt, expression: string);
  12.       begin
  13.         parser.expression := expression;
  14.         writeln (tag, ': ', expression, ' = ', format (fmt, [parser.evaluate.resFloat]));
  15.       end;
  16.  
  17.     begin
  18.  
  19.       do_sample ('1', '%f', '44950-7693*4.77');
  20.       do_sample ('2', '%f', '(44950-7693)*4.77');
  21.       do_sample ('3', '%f', '44950-(7693*4.77)');
  22.       do_sample ('4', '%g', '44950-7693*4.77');
  23.       do_sample ('5', '%e', '44950-7693*4.77');
  24.  
  25.       parser.free;
  26.     end;
  27.  
  28.   function newParser: TFPExpressionParser;
  29.     begin
  30.       result := TFPExpressionParser.create (nil);
  31.       result.builtIns := [bcMath];
  32.     end;
  33.  
  34. begin
  35.   primary (newParser);
  36. end.

Code: Text  [Select][+][-]
  1. $ fpc lazforum/60783/float_precision.pas && ./lazforum/60783/float_precision
  2. Free Pascal Compiler version 3.3.1 [2022/10/01] for x86_64
  3. Copyright (c) 1993-2022 by Florian Klaempfl and others
  4. Target OS: Linux for x86-64
  5. Compiling lazforum/60783/float_precision.pas
  6. Linking lazforum/60783/float_precision
  7. 36 lines compiled, 0.4 sec, 658752 bytes code, 361144 bytes data
  8. 1: 44950-7693*4.77 = 8254.39
  9. 2: (44950-7693)*4.77 = 177715.89
  10. 3: 44950-(7693*4.77) = 8254.39
  11. 4: 44950-7693*4.77 = 8254.3900000000067
  12. 5: 44950-7693*4.77 = 8.2543900000000067E+003

Results from 1 or 3 are what you are looking for.
« Last Edit: October 02, 2022, 10:45:16 am by Bogen85 »

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: A computational problem
« Reply #6 on: October 02, 2022, 03:26:50 pm »
Currency type
WriteStr(.... with Format speciifiers)

Fixed Point math unit etc.

The only true wisdom is knowing you know nothing

J-G

  • Hero Member
  • *****
  • Posts: 953
Re: A computational problem
« Reply #7 on: October 02, 2022, 06:51:24 pm »
I've taken to using my own two procs. to circumvent this issue.

In case it may be of interest :

Code: Pascal  [Select][+][-]
  1. Var
  2.   TmpStr :    String;
  3.  
  4. procedure StripZero(Var S : String;NoTrail : Boolean);
  5. begin
  6.   while S[length(S)] = '0' do
  7.     begin
  8.       S := copy(S,1,Pred(Length(S)));
  9.     end;
  10.   if S[length(S)] ='.' then
  11.     begin
  12.       if NoTrail then
  13.         S := copy(S,1,Pred(Length(S)))
  14.       else
  15.         S := S+'0';
  16.     end;
  17.   if Length(S) = 0 then
  18.     S := '0';
  19. end;
  20.  
  21. procedure MakeStr(Val : single;Trail : boolean;DP : byte);
  22. begin
  23.   Str(Val:4:DP,TmpStr);
  24.   StripZero(TmpStr,trail);
  25. end;
  26.  
FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: A computational problem
« Reply #8 on: October 02, 2022, 08:14:45 pm »
I've taken to using my own two procs. to circumvent this issue.

In case it may be of interest :

Code: Pascal  [Select][+][-]
  1. Var
  2.   TmpStr :    String;
  3.  
  4. procedure StripZero(Var S : String;NoTrail : Boolean);
  5. begin
  6.   while S[length(S)] = '0' do
  7.     begin
  8.       S := copy(S,1,Pred(Length(S)));
  9.     end;
  10.   if S[length(S)] ='.' then
  11.     begin
  12.       if NoTrail then
  13.         S := copy(S,1,Pred(Length(S)))
  14.       else
  15.         S := S+'0';
  16.     end;
  17.   if Length(S) = 0 then
  18.     S := '0';
  19. end;
  20.  
  21. procedure MakeStr(Val : single;Trail : boolean;DP : byte);
  22. begin
  23.   Str(Val:4:DP,TmpStr);
  24.   StripZero(TmpStr,trail);
  25. end;
  26.  

Ah, you mean this
Code: Pascal  [Select][+][-]
  1. RemoveTrailingChars(S,['0','.']);  
  2.  

and this
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. Var
  3.   S:String;
  4. begin
  5.    WriteStr(S,8254.000000001:0:2);
  6.    RemoveTrailingChars(S,['0','.']);
  7.    Caption := S;
  8. end;                                    
  9.  

Etc
 :o
The only true wisdom is knowing you know nothing

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: A computational problem
« Reply #9 on: October 02, 2022, 08:28:22 pm »
To add a little bit to it

Code: Pascal  [Select][+][-]
  1. Function RemoveTrailingCharsF(S:String; C:TSysCharSet;EmptyStr:String='0'):String;
  2. Begin
  3.    RemoveTrailingChars(S,C);
  4.    If S ='' then S :=EmptyStr;
  5.    Result := S;
  6. end;
  7.  
  8. procedure TForm1.Button1Click(Sender: TObject);
  9. Var
  10.   S:String;
  11. begin
  12.    WriteStr(S,8254.000000001:0:2);
  13.    Caption := RemoveTrailingCharsF(S,['0','.']);
  14. end;                                                    
  15.  
  16.  
The only true wisdom is knowing you know nothing

Arioch

  • Sr. Member
  • ****
  • Posts: 421
Re: A computational problem
« Reply #10 on: October 02, 2022, 08:55:51 pm »
   If S ='' then S :=EmptyStr;
end;

but why???

with short strings this is 
Code: Pascal  [Select][+][-]
  1. if s[0] = #0 then s[0] := #0;

with long strings this should be equal to 
Code: Pascal  [Select][+][-]
  1. if s = nil then s := nil;

in both cases nothing was gained...

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: A computational problem
« Reply #11 on: October 02, 2022, 09:22:02 pm »
now just think about that for a moment.

Why limit the function?
The only true wisdom is knowing you know nothing

Arioch

  • Sr. Member
  • ****
  • Posts: 421
Re: A computational problem
« Reply #12 on: October 02, 2022, 09:35:26 pm »
Ouch...

Code: Pascal  [Select][+][-]
  1. EmptyStr:String='0'):

EmptyStr is parameter here....

There is RTL constant with same name AFAIR

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: A computational problem
« Reply #13 on: October 02, 2022, 10:08:44 pm »
What if I wanted the Empty Value to be something other than a single digit char ?

what if it's a UTF8 for special symbols etc.?
The only true wisdom is knowing you know nothing

lazpas

  • Jr. Member
  • **
  • Posts: 74
Re: A computational problem
« Reply #14 on: October 26, 2022, 10:16:39 am »
Thank you all for your reply. :)

 

TinyPortal © 2005-2018