Loss of precision when using math.Max()

classic Classic list List threaded Threaded
42 messages Options
123
Reply | Threaded
Open this post in threaded view
|

Loss of precision when using math.Max()

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.

Thanks,
  Alan
--
Alan Krause
President @ Sherman & Associates, Inc.
Office: (760) 634-1700    Web: https://www.shermanloan.com/

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

Free Pascal - General mailing list
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

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

C Western-2
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


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

C Western-2
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

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

Santiago A.
In reply to this post by C Western-2
El 01/07/2018 a las 10:27, C Western escribió:

> On 29/06/18 21:55, Sven Barth via fpc-pascal wrote:
>
> 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.
Well, pascal is a hard typed language, but not that hard in numeric
issues. I think it is a little inconsistent that it implicitly converts
'0.0' to double but '0 to single.

Nevertheless, I think it is a bug. It doesn't choose the right
overloaded function

But the main is this:
you have several overload options for max
1 extended, extended
2 double, double
3 single, single
4 int64, int64
5 integer, integer

When it finds (double, single), why does  it choose (single, single)
instead of (double, double)?
The natural behavior should be to widen to the greater parameter, like
it does in expressions.

--
Saludos

Santiago A.

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

Wolf

Not so long ago, Florian was proudly bragging about "Pascal does not allow you to shoot yourself in the foot"

What about this little program:

program Project1;

var a,b: byte;
begin
  a:=1;
  b:=a*(-1);
  writeln(b);    // result: 255
end.
   

The result is obviously correct, given how the variables are declared. But there are no compiler warnings / errors that the assignment b:=a*(-1) is fishy, to put it mildly. And if you are serious about strong typing, it ought to be illegal, with a suitable complaint from the compiler.

Who is shooting whom in the foot?

Wolf


On 02/07/2018 20:22, Santiago A. wrote:
El 01/07/2018 a las 10:27, C Western escribió:
On 29/06/18 21:55, Sven Barth via fpc-pascal wrote:

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.
Well, pascal is a hard typed language, but not that hard in numeric issues. I think it is a little inconsistent that it implicitly converts '0.0' to double but '0 to single.

Nevertheless, I think it is a bug. It doesn't choose the right overloaded function

But the main is this:
you have several overload options for max
1 extended, extended
2 double, double
3 single, single
4 int64, int64
5 integer, integer

When it finds (double, single), why does  it choose (single, single) instead of (double, double)?
The natural behavior should be to widen to the greater parameter, like it does in expressions.



_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

Jim Lee



On 07/02/18 15:13, Wolf wrote:

Not so long ago, Florian was proudly bragging about "Pascal does not allow you to shoot yourself in the foot"

What about this little program:

program Project1;

var a,b: byte;
begin
  a:=1;
  b:=a*(-1);
  writeln(b);    // result: 255
end.
   

The result is obviously correct, given how the variables are declared. But there are no compiler warnings / errors that the assignment b:=a*(-1) is fishy, to put it mildly. And if you are serious about strong typing, it ought to be illegal, with a suitable complaint from the compiler.

Who is shooting whom in the foot?

Wolf




Should the compiler balk at this as well?

program Project1;

var a,b,c: byte;
begin
  a:=5;
  b:=6;
  c:=a-b;
  writeln(c);    // result: 255
end.

Without the implicit conversion of signed/unsigned values, the utility of the language is greatly diminished.

-Jim


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

C Western-2
In reply to this post by Wolf
On 02/07/18 23:13, Wolf wrote:

> Not so long ago, Florian was proudly bragging about "Pascal does not
> allow you to shoot yourself in the foot
> <http://www.toodarkpark.org/computers/humor/shoot-self-in-foot.html>"
>
> What about this little program:
>
> program Project1;
>
> var a,b: byte;
> begin
>    a:=1;
>    b:=a*(-1);
>    writeln(b);    // result: 255
> end.
>
> The result is obviously correct, given how the variables are declared.
> But there are no compiler warnings / errors that the assignment
> b:=a*(-1) is fishy, to put it mildly. And if you are serious about
> strong typing, it ought to be illegal, with a suitable complaint from
> the compiler.
>
> Who is shooting whom in the foot?
>
> Wolf
>

If you compile with range checks on, you get a runtime error.

Colin
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

Santiago A.
In reply to this post by Jim Lee
El 03/07/2018 a las 01:26, Jim Lee escribió:



On 07/02/18 15:13, Wolf wrote:

Not so long ago, Florian was proudly bragging about "Pascal does not allow you to shoot yourself in the foot"

What about this little program:

program Project1;

var a,b: byte;
begin
  a:=1;
  b:=a*(-1);
  writeln(b);    // result: 255
end.
   

The result is obviously correct, given how the variables are declared. But there are no compiler warnings / errors that the assignment b:=a*(-1) is fishy, to put it mildly. And if you are serious about strong typing, it ought to be illegal, with a suitable complaint from the compiler.

Who is shooting whom in the foot?

Wolf




Should the compiler balk at this as well?

program Project1;

var a,b,c: byte;
begin
  a:=5;
  b:=6;
  c:=a-b;
  writeln(c);    // result: 255
end.

Without the implicit conversion of signed/unsigned values, the utility of the language is greatly diminished.

Let's be honest, compared to C and many other languages (included C++, that is a suicide without extra-language analyzer tools), Pascal is very type secure. For instance, many languages allow assigning a float to an integer without any problem. Moreover without being clearly specified by language definition what the compiler should do, truncate or round.

Pascal needs to break backward compatibility to advance, that is, in fact, a new language. But if pascal is struggling to survive, let alone a new language if you are not mozilla, google...


-Jim



_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


-- 
Saludos

Santiago A.

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

Wolf
In reply to this post by Jim Lee

The major shortcoming of this thread, the way I see it, is that the answer provided explains what the compiler does, but not why the key authors of Free Pascal have made these choices. What their choices achieve is a substantial watering-down of what is supposedly Pascal's most significant paradigm: strong typing. As Jim Lee points out, strong typing does limit utility - but if utility is first concern, a weakly typed language such as C would be more appropriate.

When looking at the (partial) disassembly of my little program, we see to what degree the compiler writers have sacrificed strong typing:

a:=1;
  movb   $0x1,0x22de87(%rip)        # move 1 as single byte into 8 bit wide variable A
b:=a*(-1);
  movzbl 0x22de80(%rip),%eax        # move A into register %EAX and convert to 32 bit
  neg    %rax                       # negate what is in %EAX
  mov    %al,0x22de87(%rip)         # extract the low 8 bit from %EAX and store it in variable B
writeln(b);    // result: 255

. . .

This was compiled without any optimizations. As you can see, the brackets are ignored, as is the fact that variables A and B were supposed to be multiplied. In other words, the compiler has optimized the code, where it was not supposed to do so. It has also replaced byte typed values with longint typed values. It has taken my code and translated it as if I had written
  var
    a: byte;
    b: longint;

  begin
    a:=1;
    b:=-longint(a);          // convert A to a longint and negate it, then save result in B
    writeln( (Lower(b) );    // 'Lower' is a fictional typecast to denote that I only use the %AL portion of the %EAX register for the result
  end.

Which is quite a bit different from what I did program. Sorry if I am picky here, but this is the type of bug you can expect in software if you test using examples, and not through rigorous reasoning. And this is the reason why the original Borland Pascal had range checking built-in. If you activate it, the compiler does complain, both on my little program and on Jim's.
But by now, range checking is optional, and Lazarus at least does not even activate it by default.
But range checking is not the same as type checking, so I regard it as a crutch, a work-around that needs to be taken because the compiler does not adhere to (the spirit of) strong typing. And in this sense, what I submit here represents the same issue as what is given in the subject string if the whole thread:

Strong typing, and also readability, has been sacrificed on the altar of utility, by using implicit type conversions.

Maybe we do get some views from the key authors of Free Pascal.


Wolf


PS.: while composing this mail, Santiago wrote:  Pascal needs to break backward compatibility to advance, that is, in fact, a new language. But if pascal is struggling to survive, let alone a new language if you are not mozilla, google...

In which direction should Free Pascal move - lower type (range, overflow, memory) checking demands, with the implied additional sources for bugs, but also better speed and shorter code, a la C, or should Free Pascal rather take the lead and move towards safer, and more trustworthy, code, a la Rust?

W.



On 03/07/2018 11:26, Jim Lee wrote:



On 07/02/18 15:13, Wolf wrote:

Not so long ago, Florian was proudly bragging about "Pascal does not allow you to shoot yourself in the foot"

What about this little program:

program Project1;

var a,b: byte;
begin
  a:=1;
  b:=a*(-1);
  writeln(b);    // result: 255
end.
   

The result is obviously correct, given how the variables are declared. But there are no compiler warnings / errors that the assignment b:=a*(-1) is fishy, to put it mildly. And if you are serious about strong typing, it ought to be illegal, with a suitable complaint from the compiler.

Who is shooting whom in the foot?

Wolf




Should the compiler balk at this as well?

program Project1;

var a,b,c: byte;
begin
  a:=5;
  b:=6;
  c:=a-b;
  writeln(c);    // result: 255
end.

Without the implicit conversion of signed/unsigned values, the utility of the language is greatly diminished.

-Jim



_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

Santiago A.
El 03/07/2018 a las 14:33, Wolf escribió:

>
>
> PS.: while composing this mail, Santiago wrote:  Pascal needs to break
> backward compatibility to advance, that is, in fact, a new language.
> But if pascal is struggling to survive, let alone a new language if
> you are not mozilla, google...
>
> In which direction should Free Pascal move - lower type (range,
> overflow, memory) checking demands, with the implied additional
> sources for bugs, but also better speed and shorter code, a la C, or
> should Free Pascal rather take the lead and move towards safer, and
> more trustworthy, code, a la Rust?

Well, I am more for safer. But the problem is not that Pascal is not
safer enough (some parts could be improved, but it has a good mark) it
is about new features that need convoluted workarounds or libraries and
should be part of the language syntax.
For instance: Some functional programing, closures, anonymous functions,
concurrency, a clear use of character sets, different types of pointers.

And there are things that I would change in the current syntax, but I
suppose it is a matter of  taste.

This is a topic for fpc-other ;-)

--
Saludos

Santiago A.

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

Ralf Quint
In reply to this post by Santiago A.
On 7/3/2018 5:01 AM, Santiago A. wrote:
> El 03/07/2018 a las 01:26, Jim Lee escribió:
>>
>> Without the implicit conversion of signed/unsigned values, the
>> utility of the language is greatly diminished.
Bollocks! Just learn to program in Pascal, not trying to have Pascal act
just like another compiler/language....

>
> Let's be honest, compared to C and many other languages (included C++,
> that is a suicide without extra-language analyzer tools), Pascal is
> very type secure. For instance, many languages allow assigning a float
> to an integer without any problem. Moreover without being clearly
> specified by language definition what the compiler should do, truncate
> or round.
>
> Pascal needs to break backward compatibility to advance, that is, in
> fact, a new language. But if pascal is struggling to survive, let
> alone a new language if you are not mozilla, google...
Again bollocks!

If you have properly range check enabled, you get an exception at those
code samples. Such for code samples (all variables of the same type,
Byte in the examples), the compiler can not make a determination at
compile time (and we have a compiler here, not just another interpreter,
like Python, etc ).

And no "new language" can absolve the programmer from properly doing
their work. Everything else is just a quick hack, not a properly
designed program...

Ralf

---
This email has been checked for viruses by Avast antivirus software.
https://www.avast.com/antivirus

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

vojtech.cihak
In reply to this post by Jim Lee

Hi,

 

I agree with the original topic that overload function Math.Max(Double, Double) shoud be chosen instead of Math.Max(Single, Single) when one of parameters is Integer.

 

But your criticism make no sense to me. You can as well point that this code:

 

var a, b: Byte;

begin

  a:=5;

  b:=a div 2;

  writeln(b);  // result: 2

end;

 

will not give any warning even if correct result is 2.5.

It is simply absurd because it is not about shooting your own foot. Compiler is not a crystal ball, it does what you tell him.

If you need floating point, use floating point types and floating point division (my example) and if you need signed results, use signed types (your example).

 

Vojtěch

______________________________________________________________

> Od: Wolf <[hidden email]>
> Komu: [hidden email]
> Datum: 03.07.2018 14:33
> Předmět: Re: [fpc-pascal] Loss of precision when using math.Max()
>

On 03/07/2018 11:26, Jim Lee wrote:

 


On 07/02/18 15:13, Wolf wrote:

Not so long ago, Florian was proudly bragging about "Pascal does not allow you to shoot yourself in the foot"

What about this little program:

program Project1;

var a,b: byte;
begin
  a:=1;
  b:=a*(-1);
  writeln(b);    // result: 255
end.
   

The result is obviously correct, given how the variables are declared. But there are no compiler warnings / errors that the assignment b:=a*(-1) is fishy, to put it mildly. And if you are serious about strong typing, it ought to be illegal, with a suitable complaint from the compiler.

Who is shooting whom in the foot?

Wolf



Should the compiler balk at this as well?

program Project1;

var a,b,c: byte;
begin
  a:=5;
  b:=6;
  c:=a-b;
  writeln(c);    // result: 255
end.

Without the implicit conversion of signed/unsigned values, the utility of the language is greatly diminished.

-Jim



_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal



----------

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

wkitty42
In reply to this post by C Western-2
On 07/03/2018 03:26 AM, C Western wrote:
> On 02/07/18 23:13, Wolf wrote:
>> Who is shooting whom in the foot?
>>
>> Wolf
>>
>
> If you compile with range checks on, you get a runtime error.


why are so many folks NOT developing with all the checks (range, heap, stack,
etc) turned ON and then turning them off for production builds??? that is
another one of ""the Pascal ways"" AFAIK... we've (TINW) been doing this since
Pascal came out with these checks... it just makes no sense not to use the tools
at hand and these checks are important tools... even moreso in today's world...


--
  NOTE: No off-list assistance is given without prior approval.
        *Please keep mailing list traffic on the list unless*
        *a signed and pre-paid contract is in effect with us.*
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

Marco van de Voort
In reply to this post by Santiago A.
In our previous episode, Santiago A. said:
>
> Pascal needs to break backward compatibility to advance, that is, in
> fact, a new language. But if pascal is struggling to survive, let alone
> a new language if you are not mozilla, google...

I think to advance Pascal needs less discussion about language and more
about libraries.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

Jim Lee
In reply to this post by Wolf



On 07/03/18 05:33, Wolf wrote:

The major shortcoming of this thread, the way I see it, is that the answer provided explains what the compiler does, but not why the key authors of Free Pascal have made these choices. What their choices achieve is a substantial watering-down of what is supposedly Pascal's most significant paradigm: strong typing. As Jim Lee points out, strong typing does limit utility - but if utility is first concern, a weakly typed language such as C would be more appropriate.

When looking at the (partial) disassembly of my little program, we see to what degree the compiler writers have sacrificed strong typing:

a:=1;
  movb   $0x1,0x22de87(%rip)        # move 1 as single byte into 8 bit wide variable A
b:=a*(-1);
  movzbl 0x22de80(%rip),%eax        # move A into register %EAX and convert to 32 bit
  neg    %rax                       # negate what is in %EAX
  mov    %al,0x22de87(%rip)         # extract the low 8 bit from %EAX and store it in variable B
writeln(b);    // result: 255

. . .

This was compiled without any optimizations. As you can see, the brackets are ignored, as is the fact that variables A and B were supposed to be multiplied. In other words, the compiler has optimized the code, where it was not supposed to do so. It has also replaced byte typed values with longint typed values. It has taken my code and translated it as if I had written
  var
    a: byte;
    b: longint;

  begin
    a:=1;
    b:=-longint(a);          // convert A to a longint and negate it, then save result in B
    writeln( (Lower(b) );    // 'Lower' is a fictional typecast to denote that I only use the %AL portion of the %EAX register for the result
  end.

Which is quite a bit different from what I did program. Sorry if I am picky here, but this is the type of bug you can expect in software if you test using examples, and not through rigorous reasoning. And this is the reason why the original Borland Pascal had range checking built-in. If you activate it, the compiler does complain, both on my little program and on Jim's.
But by now, range checking is optional, and Lazarus at least does not even activate it by default.
But range checking is not the same as type checking, so I regard it as a crutch, a work-around that needs to be taken because the compiler does not adhere to (the spirit of) strong typing. And in this sense, what I submit here represents the same issue as what is given in the subject string if the whole thread:

Strong typing, and also readability, has been sacrificed on the altar of utility, by using implicit type conversions.

Maybe we do get some views from the key authors of Free Pascal.


Wolf


I didn't fully understand the intent of your first post, but now I get what you're saying.

I tend to agree.  Strict typing is the main thing that separates Pascal from C, conceptually.  I'd rather not see them converge.

-Jim



_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

wkitty42
In reply to this post by Ralf Quint
On 07/03/2018 12:41 PM, Ralf Quint wrote:
> And no "new language" can absolve the programmer from properly doing their
> work. Everything else is just a quick hack, not a properly designed
> program...


Welcome Back, Ralf!  we've missed you O:) O:) O:)



--
  NOTE: No off-list assistance is given without prior approval.
        *Please keep mailing list traffic on the list unless*
        *a signed and pre-paid contract is in effect with us.*
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

Martok
In reply to this post by wkitty42
>> If you compile with range checks on, you get a runtime error.
> why are so many folks NOT developing with all the checks (range, heap, stack,
> etc) turned ON and then turning them off for production builds???
Actually, while we're at it - it seems to me that for FPC, "all runtime checks
enabled" is the "defined" way to use the language, and disabling them is more of
an optimization that the programmer may choose?
In books about TP and Delphi, it is usually presented the other way around, the
checks being a debugging tool for edge cases and not essential.

If so, that'd explain some of the issues people have.

--
Regards,
Martok


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
gtt
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

gtt
In reply to this post by vojtech.cihak

Zitat von Vojtěch Čihák <[hidden email]>:
> will not give any warning even if correct result is 2.5.
> It is simply absurd because it is not about shooting your own foot.  
> Compiler is not a crystal ball, it does what you tell him.
> If you need floating point, use floating point types and floating  
> point division (my example) and if you need signed results, use

Really?

There are other source of loss of precision (in the trunk version) and  
the compiler does
not what I tell him. Example:

const
   d1: double = 1.0/3.0;
   d2: double = 1/3;
   x1: extended = 1.0/3.0;
   x2: extended = 1/3;
   s1: single   = 1.0/3.0;
   s2: single   = 1/3;
begin
   writeln(s1:30:10,  s2:30:10);
   writeln(d1:30:16,  d2:30:16);
   writeln(x1:30:16,  x2:30:16);
end.

and the result

                   0.3333333433                  0.3333333433
             0.3333333432674408            0.3333333333333333
             0.3333333432674408            0.3333333333333333

The single result is expected. But the  double and extended constants  
d1, x1 are
evaluated as single, even if I told the compiler to use the floating  
point quotient 1.0/3.0.

If I use the integer quotient the values are as expected. This is  
against intuition and gives
no warning. And even if I can adapt to this and live with this quirk:  
Is there a definite
statement, that is will remain so. (Delphi works as expected).


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loss of precision when using math.Max()

Florian Klämpfl
In reply to this post by Wolf
Am 03.07.2018 um 14:33 schrieb Wolf:
>
> Maybe we do get some views from the key authors of Free Pascal.

Not from me. These topics have been discussed zillion times.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
123