Search code examples
delphiintegerdelphi-10.2-tokyoconstant-expression

What is the logic behind the bitwise not operator on constant integer values?


This question may sound silly but I can not understand the idea behind the not operator on constant integers. I get the following results:

  • not $FF => $FF00
  • not $FFFF => $FFFF0000
  • not $FFFFFFFF => $00
  • not $FFFFFFFFFFFFFFFF => $00

The first two values look wrong to me.

The documentation states:

For example, not performs bitwise negation on an integer operand

and later:

The result of a not operation is of the same type as the operand

This is not in line with the observed behaviour.

Full code example:

unit Unit5;

interface

procedure c();

implementation

uses Vcl.Dialogs, System.SysUtils;

procedure Invert(v: ShortInt); overload; begin
  ShowMessage('ShortInt $' + v.ToHexString());
end;

procedure Invert(v: SmallInt); overload; begin
  ShowMessage('SmallInt $' + v.ToHexString());
end;

procedure Invert(v: Integer); overload;
begin
  ShowMessage('Integer $' + v.ToHexString());
end;

procedure c();
const
  byteValue = not $FF; // = $FF00
  wordValue = not $FFFF; // = $FFFF0000
  cardValue = not $FFFFFFFF; // = $00
  uint64Value = not $FFFFFFFFFFFFFFFF; // = $00
begin
  Invert(byteValue);
  Invert(wordValue);
  Invert(cardValue);
  Invert(uint64Value);
end;

end.

Solution

  • The compiler is free* to pick a suitable type for true constants, if there can be an ambiguity.

    You need to help out in this case by using a value typecast:

    const
       byteValue = Byte(not $FF);  // => $00  (ShortInt)
       wordValue = Word(not $FFFF); // => $00 (ShortInt) 
    

    To avoid overloaded confusion, since you only offer signed printout alternatives:

    const
       byteValue = ShortInt(not $FF);  // => $00 (ShortInt)
       wordValue = SmallInt(not $FFFF); // => $0000 (SmallInt) 
    

    When no direct overloaded procedure matches an ordinal type, it can be difficult to predict which overload the compiler selects.


    From Declared_Constants#True_Constants:

    The syntax for declaring a true constant is:

    const identifier = constantExpression
    

    where identifier is any valid identifier and constantExpression is an expression that the compiler can evaluate without executing your program.

    If constantExpression returns an ordinal value, you can specify the type of the declared constant using a value typecast.



    Added a full test:

    program Project110;
    
    {$APPTYPE CONSOLE}
    
    uses
      System.SysUtils;
    
    procedure Invert(v: ShortInt); overload; begin
      WriteLn('ShortInt $' + v.ToHexString());
    end;
    
    procedure Invert(v: SmallInt); overload; begin
      WriteLn('SmallInt $' + v.ToHexString());
    end;
    
    procedure Invert(v: Integer); overload;
    begin
      WriteLn('Integer $' + v.ToHexString());
    end;
    
    procedure Invert(v: Int64); overload;
    begin
      WriteLn('Int64 $' + v.ToHexString());
    end;
        
    procedure c();
    const
      byteValue = ShortInt(not $FF); // = ShortInt $00
      wordValue = SmallInt(not $FFFF); // = SmallInt $0000
      cardValue = Integer(not $FFFFFFFF); // = Integer $00000000
      uint64Value = Int64(not $FFFFFFFFFFFFFFFF); // = Int64 $0000000000000000
    begin
      Invert(byteValue);
      Invert(wordValue);
      Invert(cardValue);
      Invert(uint64Value);
    end;
    
    begin
      c;
      ReadLn;
    end.