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

C Western l at c-m-w.me.uk
Mon Jul 2 09:44:52 CEST 2018


On 01/07/18 09:27, C Western wrote:
> 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
> 

And if an integer variable is used, Max(Single, Single) is called:

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

Prints:

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

Delphi prints Double for all 4. I think the last case is definitely a bug.

Colin




More information about the fpc-pascal mailing list