Delphi Warning W1073 Combining signed type and unsigned 64-bit type - treated as an unsigned type

Viewed 369

I get the subject warning on the following line of code;

SelectedFilesSize := SelectedFilesSize +
  UInt64(IdList.GetPropertyValue(TShellColumns.Size)) *
  ifthen(Selected, 1, -1);

Specifically, the IDE highlights the third line.

SelectedFilesSize is declared as UInt64.

The code appears to work when I run it; if I select an item, its file size is added to the total, if I deselect a file its size is subtracted.

I know I can suppress this warning with {$WARN COMBINING_SIGNED_UNSIGNED64 OFF}.

Can someone explain? Will there be an unforeseen impact if SelectedFilesSize gets huge? Or an impact on a specific target platform?

Delphi 10.3, Win32 and Win64 targets

1 Answers

This will work here, but the warning is right.

If you multiply a UInt64 with -1, you are actually multiplying it with $FFFFFFFFFFFFFFFF. The final result will be a 128 bit value, but the lower 64 bits will be the same as for a signed multiplication (that is also why the code generator often produces an imul opcode, even for unsiged multiplication: the lower bits will be correct, just the — unused — higher bits won't be). The upper 64 bits won't be used anyway, so they don't matter.

If you add that (actually negative) value to another UInt64 (e.g. SelectedFilesSize), the 64 bit result will be correct again. The CPU does not discriminate between positive or negative values when adding. The resulting CPU flags (carry, overflow) will indicate overflow, but if you ignore that by not using range or overflow checks, your code will be fine.

Your code will likely produce a runtime error if range or overflow checks are on, though.

In other words, this works because any excess upper bit — the 64th bit and above — can be ignored. Otherwise, the values would be wrong. See example.

Example

Say your IdList.GetPropertyValue(TShellColumns.Size) is 420. Then you are performing:

$00000000000001A4 * $FFFFFFFFFFFFFFFF = $00000000000001A3FFFFFFFFFFFFFF5C

This is a huge but positive number, but fortunately the lower 64 bits ($FFFFFFFFFFFFFF5C) can be interpreted as -420 (a really negative value in 128 bit would be $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C or -420).

Now say your SelectedFileSize is 100000 (or hex $00000000000186A0). Then you get:

$00000000000186A0 + $FFFFFFFFFFFFFF5C = $00000000000184FC 
(or actually $100000000000184FC, but the top bit -- the carry -- is ignored).

$00000000000184FC is 99580 in decimal, so exactly the value you wanted.

Related