[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