Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2018-02

Abs(Variant) unexpected result

15 messages · 4 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Bart

Hi,

See: http://forum.lazarus.freepascal.org/index.php/topic,40223.msg277657/

This seems rather unexpected.
Is it a bug?

program v;
{$ifdef windows}
{$apptype console}
{$endif}
uses
  variants;
var
  X: Variant;
  B: Boolean;
begin
  X := -1.5;
  writeln('X = ',X);
  B := VarIsFloat(X);
  writeln('VarIsFloat  : ',B);
  B := VarIsNumeric(X);
  writeln('VarIsNumeric: ',B);
  B := VarIsOrdinal(X);
  writeln('VarIsOrdinal: ',B);
  X := Abs(X);
  writeln('After Abs()');
  writeln('X = ',X);
  B := VarIsFloat(X);
  writeln('VarIsFloat  : ',B);
  B := VarIsNumeric(X);
  writeln('VarIsNumeric: ',B);
  B := VarIsOrdinal(X);
  writeln('VarIsOrdinal: ',B);
end.

Outputs:
C:\Users\Bart\LazarusProjecten\bugs\Console\variants>v X = -1,5
VarIsFloat : TRUE
VarIsNumeric: TRUE
VarIsOrdinal: FALSE
After Abs()
X = 2
VarIsFloat : FALSE
VarIsNumeric: TRUE
VarIsOrdinal: TRUE

Bart

Reply by email

Michael Van Canneyt
to Bart
Bart wrote 5 lines

Not really.

The abs() function is overloaded for different types.

The compiler does not know at compile time what type the variant is, how can you expect it to choose the "right" overloaded version ?

It probably takes the first fit for possible values in a Variant, which is most likely an integer version.

Don't use variants if you want type safety :)

Michael.

Reply by email

Michael Van Canneyt
to Bart
Bart wrote 7 lines
On Sun, Feb 25, 2018 at 1:01 PM, Michael Van Canneyt wrote: > The compiler does not know at compile time what type the variant is, how can > you expect it to choose the "right" overloaded version ? I would have expected that it will choose the right one @runtime .

Choices between overloads are always made at compile time.

Only if we add an

   Abs(Variant) : Variant;

which will then make the choice will this work. But I'm not sure whether this would interfere with the normal working of abs() overloads.

Michael.

Reply by email

Bart
to Michael Van Canneyt
Michael Van Canneyt wrote
Only if we add an Abs(Variant) : Variant; which will then make the choice will this work.

If the compiler accepts Abs(Variant), it should IMHO have a correct overload for this.
(Maybe in the variants unit?)

I don't know how Delphi behaves, but the official Delphi docs state that Abs() only has overloads for floats, integer and int64. If their compiler behaves as the docs say, Abs(Variant) would be a syntax error. I asked on Dutch Delphi forum if someone could test.

Bart

Reply by email

Jonas Maebe
to Bart
Bart wrote
I don't know how Delphi behaves, but the official Delphi docs state that Abs() only has overloads for floats, integer and int64. If their compiler behaves as the docs say, Abs(Variant) would be a syntax error.

No, because variant can be implicitly converted to all of those types and there are different (undocumented by Embarcadero) overload selection priorities for each of those types.

Jonas

Reply by email

Bart
to Jonas Maebe

Delphi 10.2 Tokyo:

X = -1,5
VarIsFloat : TRUE
VarIsNumeric: TRUE
VarIsOrdinal: FALSE
After Abs()
X = 1,5
VarIsFloat : TRUE
VarIsNumeric: TRUE
VarIsOrdinal: FALSE

I asked to test with "X := -1" to see if Delphi always chooses the
float overload.

Bart

Reply by email

Jonas Maebe
to Bart
Bart wrote 14 lines
Delphi 10.2 Tokyo: X = -1,5 VarIsFloat : TRUE VarIsNumeric: TRUE VarIsOrdinal: FALSE After Abs() X = 1,5 VarIsFloat : TRUE VarIsNumeric: TRUE VarIsOrdinal: FALSE I asked to test with "X := -1" to see if Delphi always chooses the float overload.

As Michael said, overloads are selected at compile time. This is true for both FPC and Delphi. We even have over a 100 unit tests that we ran under Delphi to reverse engineer their selection priorities in case of variants: https://svn.freepascal.org/svn/fpc/trunk/tests/test/cg/variants/

Abs(), however, gets a forced conversion to float in Delphi: https://bugs.freepascal.org/view.php?id=20551

Jonas

Reply by email

Bart
to Jonas Maebe
Jonas Maebe wrote 7 lines
As Michael said, overloads are selected at compile time. This is true for both FPC and Delphi. We even have over a 100 unit tests that we ran under Delphi to reverse engineer their selection priorities in case of variants: https://svn.freepascal.org/svn/fpc/trunk/tests/test/cg/variants/ Abs(), however, gets a forced conversion to float in Delphi: https://bugs.freepascal.org/view.php?id=20551

Hmm, did not find that one (I searched bugtracker before posting here).

Seems you are right:

Delphi Tokyo 10.2

X = -1
VarIsFloat : FALSE
VarIsNumeric: TRUE
VarIsOrdinal: TRUE
After Abs()
X = 1
VarIsFloat : TRUE
VarIsNumeric: TRUE
VarIsOrdinal: FALSE

So, would it be possible to have an overloaded Abs(V: Variant): Variant; function in the variants unit?

To be clear. Personally I don't need it (at least not ATM), I'm just curious.

Thanks for explaining.

Bart

Bart

Reply by email

Michael Van Canneyt
to Bart
Bart wrote 29 lines
On Sun, Feb 25, 2018 at 5:01 PM, Jonas Maebe wrote: > As Michael said, overloads are selected at compile time. This is true for > both FPC and Delphi. We even have over a 100 unit tests that we ran under > Delphi to reverse engineer their selection priorities in case of variants: > https://svn.freepascal.org/svn/fpc/trunk/tests/test/cg/variants/ > > Abs(), however, gets a forced conversion to float in Delphi: > https://bugs.freepascal.org/view.php?id=20551 Hmm, did not find that one (I searched bugtracker before posting here). Seems you are right: Delphi Tokyo 10.2 X = -1 VarIsFloat : FALSE VarIsNumeric: TRUE VarIsOrdinal: TRUE After Abs() X = 1 VarIsFloat : TRUE VarIsNumeric: TRUE VarIsOrdinal: FALSE So, would it be possible to have an overloaded Abs(V: Variant): Variant; function in the variants unit?

I advise against it.

S : String;
begin
   S:='My very nice string';
   S:=Abs(S);
end;

Will then compile and give a run-time error, as opposed to a compile-time error now.

Michael.

Reply by email

Bart
to Michael Van Canneyt
Michael Van Canneyt wrote 15 lines
> So, would it be possible to have an overloaded Abs(V: Variant): > Variant; function in the variants unit? I advise against it. S : String; begin S:='My very nice string'; S:=Abs(S); end; Will then compile and give a run-time error, as opposed to a compile-time error now.

Did not think about that.
Yeah, that's really bad.

So, all we can do is let the compiler pick the float version for Abs(Variant)?

Bart

Reply by email

Michael Van Canneyt
to Bart
Bart wrote 23 lines
On Sun, Feb 25, 2018 at 5:34 PM, Michael Van Canneyt wrote: >> So, would it be possible to have an overloaded Abs(V: Variant): >> Variant; function in the variants unit? > > > I advise against it. > > S : String; > > begin > S:='My very nice string'; > S:=Abs(S); > end; > > Will then compile and give a run-time error, as opposed to a compile-time > error now. Did not think about that. Yeah, that's really bad. So, all we can do is let the compiler pick the float version for Abs(Variant)?

It seems so. Better yet, don't use variants.
They violate what Pascal stands for: type safety.

If you really _must_ use them, do any conversions explicitly.

Michael.

Reply by email

Bart
to Michael Van Canneyt
Michael Van Canneyt wrote 5 lines
> So, all we can do is let the compiler pick the float version for > Abs(Variant)? It seems so.

OK, for D compatibilty (untill they change that).

Better yet, don't use variants. They violate what Pascal stands for: type safety. If you really _must_ use them, do any conversions explicitly.

Point taken.

Bart

Reply by email

Rik van Kekem
to Michael Van Canneyt: It seems so. Better yet, don't use variants. They violate what Pascal stands for: type…
Michael Van Canneyt wrote 11 lines
On Sun, 25 Feb 2018, Bart wrote: > On Sun, Feb 25, 2018 at 5:34 PM, Michael Van Canneyt > wrote: > > So, all we can do is let the compiler pick the float version for > Abs(Variant)? It seems so. Better yet, don't use variants. They violate what Pascal stands for: type safety. If you really _must_ use them, do any conversions explicitly.

Additionally, A warning when using variant with Abs() would be useful then.

  var
     X:Variant;
  begin
     X:=5.5;
     writeln('X = ',Abs(X));// <-- gives 6

Rik

Reply by email

Bart
to Michael Van Canneyt: I advise against it. S : String; begin S:='My very nice string'; S:=Abs(S); end; Will…
Michael Van Canneyt wrote 15 lines
> So, would it be possible to have an overloaded Abs(V: Variant): > Variant; function in the variants unit? I advise against it. S : String; begin S:='My very nice string'; S:=Abs(S); end; Will then compile and give a run-time error, as opposed to a compile-time error now.
Suggestion from the forum:
function Abs(var V: Variant): Variant; overload;
begin                                        ;
  If (VarIsNumeric(V)) then
  begin
    if V< 0 then
      Result := -V
    else
      Result := V
  end
  else
   Raise EVariantBadVarTypeError.Create(SomeMessage);
end;

While this will give hints if you did not initialize V, it will not let you compile Abs(AString).

Bart

Reply by email