[fpc-pascal] Currency constant wrongly stored in generated EXE?

Bruno Krayenbuhl kraybruno at bluewin.ch
Tue May 13 14:17:21 CEST 2014


Lets restate the hole thing, considering unit ncon.pas and pexpr.pas units 
in FPC 2.6.4
compiler.

1° It is not possible, without using some ad hoc adjustements, to have
always an EXACT CURRENCY stored in a DOUBLE or EXTENDED because Double or
Extended being expressed as Sign*2^exp*Base2(n). Just start with 0.1 to see
what I mean.

2° trealconstnode class stores the currency_literal value to value_real
(type bestreal) then converts it to value_currency either in the
trealconstnode.create or sets it in pexpr.pas  function
factor(getaddr,typeonly:boolean) : tnode; just after {$ifdef
FPC_HAS_STR_CURRENCY}

3° c:=92233720368547; // The original problem
multiple operations involve *10000 (OK per see) and /10000 (not so good)
while parsing the constant to bestreal type before generating .EXE code.
(bestreal is Extended on Intel I386 and Up and Double on ARM).

4° The compiler will not compain about Currency_Value:=0.99999 whereas the
max decimal precision is 0.9999 for decimal part.

5° What I found is that only + or - operations on Currency's giving a
Currency will be generated with plain Int64 addition and subtractions.

Now what ?

It would be interesting that someone who has an ARM based machine/compiler 
do
some testing.

Since currency is a very good type for accounting, you know
Sum(Assets)-Sum(Liabilities)=Sum(Incomes)-Sum(Costs) -> hopefully benefit 
and exact,
some workarounds must be found.
A basic way to circumvent the risks when absolutly necessary would be 
something like what is decribed next, not very Pascal clean I must admit :

- Insure that the compiler will not see a literal currency when assigning or
calculating  a precise value containing a literal currency value.

so short example, look at the ASM code generated.

var
 c: currency;
 lInt64:Int64 absolute c; // Avoid use of Currency
begin
  c:=0.99999; // Accepted by the compiler ... and rounded to 1
  c:=0.99986; // Accepted by the compiler ... and rounded to 0.9999
  c:=0.99984; // Accepted by the compiler ... and rounded to 0.9998
  { The easy way but uses the FPU }
  c:=92233720368547; // <- that might be wrongly evaluated by the Compiler 
(FPU usage) but nothing will be visible in the generated ASM regarding FPU 
evaluation, except in your case, an invalid HEX value
  c:=c+0.01; // <- uses FPU
  writeln('c+0.01 with FPU=', c);
  { Clumsy but no FPU usage while compiling and executing }
  lInt64:=922337203685470000; // <- You can test but my bet is that it 
probably always correctly evaluated by the compiler
  lInt64:=lInt64+100; // Equivalent to c:=c+0.01
  writeln('c+0.01 indirect no FPU=', c);
  readln;
end;

Your subject has been interesting and I'll be careful now with rounding and 
Currency.

Regard Bruno.




More information about the fpc-pascal mailing list