[fpc-pascal] Loss of precision when using math.Max()

C Western l at c-m-w.me.uk
Sun Jul 1 10:27:33 CEST 2018


On 29/06/18 21:55, Sven Barth via fpc-pascal wrote:
> Am 29.06.2018 um 18:45 schrieb Alan Krause:
>> I stumbled upon something the other day that was causing numerical 
>> differences between compiled Delphi and FPC code. Executing the 
>> following sample console application illustrates the issue clearly:
>>
>> program test;
>>
>> uses
>>   math, SysUtils;
>>
>> var
>>   arg1 : double;
>>   arg2 : double;
>>   res  : double;
>> begin
>>   arg1 := 100000.00;
>>   arg2 := 72500.51;
>>   writeln( 'arg1 = ' + FormatFloat( '0.00000000', arg1 ) );
>>   writeln( 'arg2 = ' + FormatFloat( '0.00000000', arg2 ) );
>>
>>   res := arg1 - arg2;
>>   writeln( 'arg1 - arg2 = ' + FormatFloat( '0.00000000', res ) );
>>   writeln( 'Max( arg1 - arg2, 0 ) = ' + FormatFloat( '0.00000000', 
>> Max( res, 0 ) ) );
>>   writeln( 'Max( arg1 - arg2, 0.0 ) = ' + FormatFloat( '0.00000000', 
>> Max( res, 0.0 ) ) );
>> end.
>>
>> --- begin output (Linux x86_64) ---
>>
>> arg1 = 100000.00000000
>> arg2 = 72500.51000000
>> arg1 - arg2 = 27499.49000000
>> *Max( res, 0 ) = 27499.49023438*
>> Max( res, 0.0 ) = 27499.49000000
>>
>> --- end output ---
>>
>> I am guessing that the integer value of zero is causing the wrong 
>> overloaded function to be called? I was able to solve the problem in 
>> my code by replacing the 0 with 0.0.
> 
> The compiler converts the 0 to the type with the lowest precision that 
> can hold the value (or the largest if none can hold it exactly). For 0 
> this is already satisfied by Single, so the compiler essentially has the 
> parameter types Double and Single. For some reason (I don't know whether 
> it's due to a bug or by design) it picks the Single overload instead of 
> the Double one.
> Someone who knows more about the compiler's overload handling would need 
> to answer why it favors (Single, Single) over (Double, Double) for 
> (Double, Single) parameters (or (Single, Double), the order doesn't 
> matter here).
> 
> Regards,
> Sven
> 

More confusingly, if a single variable is used, the expected Max(Double, 
Double) is called:

function Max(a, b: Double): Double; overload;
begin
   WriteLn('Double');
   if a > b then Result := a else Result := b;
end;

function Max(a, b: Single): Single; overload;
begin
   WriteLn('Single');
   if a > b then Result := a else Result := b;
end;

var
   v1: Double;
   v2: Single;
begin
   v1 := Pi;
   v2 := 0;
   WriteLn(v1);
   WriteLn(Max(v1,0));
   WriteLn(Max(v1,0.0));
   WriteLn(Max(v1,v2));
end.

Prints:
  3.1415926535897931E+000
Single
  3.141592741E+00
Double
  3.1415926535897931E+000
Double
  3.1415926535897931E+000

If this is not a bug, it would be very helpful if the compiler could 
print a warning whenever a value is implicitly converted from double to 
single.

Colin





More information about the fpc-pascal mailing list