7
votes
 Result:= b8;
 Result:= Result+ (b7  SHL  8);
 Result:= Result+ (b6  SHL 16);
 Result:= Result+ (b5  SHL 24);
 Result:= Result+ (b4  SHL 32);
 Result:= Result+ (b3  SHL 40);      <------ here
 Result:= Result+ (b2  SHL 48);
 Result:= Result+ (MSB SHL 56);

(Result is int64 and b1-b8 is byte).

But the compiler complains about "constant exp violates subrange bounds". Then I have found this warning "The compiler will reject hardcoded shift right values that exceed 32, unless the data type is Int64. The same is not true for Shl" on a web site. Delphi Help says nothing about it. Why is there a limit? Can I stop it? Maybe a compiler directive or something? I can use a multiplication to get over it, but it will be slower. There is a faster way to do it?

Edit: I am thinking also in creating two cardinals because I can use SHL on cardinals, then to merge the cardinals together. This will require only one multiplication. Any other ideas?

Edit2: The code is part of an algorithm that converts a number form base 255 to base 256 (and the other way around). I do this 50000 million times; so speed is important.

4
Result is a Int64-type? And b3 also? Can you insert a typecast to Int64: Int64(b3)?Andreas
Sorry. Sorry. I totally forgot about that. I updated my question.Z80

4 Answers

17
votes

I'd avoid all arithmetic (your code has additions and shifts) and do it like this:

Int64Rec(Result).Bytes[0] := b8;
Int64Rec(Result).Bytes[1] := b7;
//etc.

Int64Rec is defined in SysUtils like this:

Int64Rec = packed record
  case Integer of
    0: (Lo, Hi: Cardinal);
    1: (Cardinals: array [0..1] of Cardinal);
    2: (Words: array [0..3] of Word);
    3: (Bytes: array [0..7] of Byte);
end;

If you stored your bytes in an array then you could wrap it all up in a for loop.

11
votes

I'm assuming that your Result is Int64 already, and b8, b7 and so on are declared as Byte. The compiler needs a little help. Typecast to Int64:

  Result := b8;
  Result := Result + (b7 shl  8);
  Result := Result + (b6 shl 16);
  Result := Result + (b5 shl 24);
  Result := Result + (Int64(b4) shl 32);
  Result := Result + (Int64(b3) shl 40);
  Result := Result + (Int64(b2) shl 48);
  Result := Result + (Int64(msb) shl 56);
8
votes

Here is another solution that uses shifts but I believe is "cleaner" (though not as clean as David's suggestion):

result := MSB;
result := (result shl 8) or b2;  { could use "shl sizeof(b2)" instead of 8 }
result := (result shl 8) or b3;
etc
result := (result shl 8) or b8;

this solution avoids all the "magic" shifting values and it is probably easier to understand. Also, if MSB..b8 (or b1..b8) were an array of bytes, the above code could easily be converted into a single line loop.

To answer your question as to why the compiler does not accept the value 40 and above, the reason is most likely due to this quote from Volume 2B of the Intel Instruction Set Reference for the SHLD instruction:

In non-64-bit modes and default 64-bit mode; only bits 0 through 4 of the count are used. This masks the count to a value between 0 and 31. If a count is greater than the operand size, the result is undefined.

The equivalent condition on the SHL instruction is not quite as stringent:

The 8086 does not mask the shift count. However, all other IA-32 processors (starting with the Intel 286 processor) do mask the shift count to 5 bits, resulting in a maximum count of 31. This masking is done in all operating modes (including the virtual-8086 mode) to reduce the maximum execution time of the instructions.

In either case, a value greater than 31 is either useless (when using SHL) or undefined (when using SHLD). The compiler obviously knows this and, it is preventing you from writing code that is potentially erroneous (in 64 bit mode.)

if you, indeed are doing this operation 50 BILLION times, then you may want to consider doing it in inline assembly, which would be quite simple:

asm
    xor eax, eax   
    or  eax, MSB   { or b1 depending on how you named the MSB }
    shl eax, 8
    or  eax, b2
    shl eax, 8
    or  eax, b3
    shl eax, 8
    or  eax, b4
    mov High32bit, eax
end;

and repeat the above operation for the low 32 bit dword and b5 thru b8. I didn't suggest using SHLD because I don't believe Delphi supports the 64 bit registers nor 64bit instructions (I could be wrong, I've never tried it.)

Note: I've heard rumors that the 64bit version of Delphi will not support inline assembly. This may or may not be the case but, I would stay away from inline assembly unless it is truly absolutely necessary.

Hope that helps,

John.

PS: there is another reason why David Heffernan's solution is the best one. In the solution I presented, every instruction depends on the previous instruction (that is, eax must be shifted by 8 before the next "or" instruction can be executed). David's solution sets individual bytes, as such, each assignment is independent of the previous assignment, this will allow the processor to potentially carry out multiple assignments in parallel. In this era of multi-core processors this has the potential of being quite a bit faster than the assembly code I gave as an example.

0
votes

Heres a function to convert a Integer to int64bit: example: AData = 36 --> Bit 36 will be set etc

uses SysUtils;

[...]

function IntegertoInt64Bit(AData : Integer): UInt64;
  var
    idx : integer;
  begin
    result := 0;
    for idx := 7 downto 0 do
    begin
      if(AData >= idx * 8) then
      begin
        Int64Rec(result).Bytes[idx] := 1 shl ((AData-1) mod 8);
        break;
      end;
    end;
  end;