Recent

Author Topic: Inconsistent behaviour with bitshift operations  (Read 1450 times)

ad1mt

  • Full Member
  • ***
  • Posts: 199
    • Mark Taylor's Home Page
Inconsistent behaviour with bitshift operations
« on: March 05, 2024, 01:06:42 pm »
EDITED
I'm getting inconsistent behaviour with bitshift operations.
Run the following program twice; once with range-checking enabled, and again with range-checking enabled:
Code: Pascal  [Select][+][-]
  1. program bitshift_demo;
  2. uses strutils;
  3. var
  4. i_16u           :uint16;
  5. i_16u_shift     :uint16;
  6. i_32u           :uint32;
  7. i_32u_shift     :uint32;
  8.  
  9. procedure shift_1;
  10. begin
  11. writeln('i_16u = ',IntToBin(i_16u,16),' ',i_16u);
  12. write('i_16u >> 1 = ');
  13. try i_16u_shift:= (i_16u >> 1); except; end;
  14. writeln(IntToBin(i_16u_shift,16),' ',i_16u_shift);
  15. writeln;
  16. writeln('i_32u = ',IntToBin(i_32u,32),' ',i_32u);
  17. write('i_32u >> 1 = ');
  18. try i_32u_shift:= (i_32u >> 1); except; end;
  19. writeln(IntToBin(i_32u_shift,32),' ',i_32u_shift);
  20. writeln;
  21. writeln('i_16u = ',IntToBin(i_16u,16),' ',i_16u);
  22. write('i_16u << 1 = ');
  23. try i_16u_shift:= (i_16u << 1); except; end;
  24. writeln(IntToBin(i_16u_shift,16),' ',i_16u_shift);
  25. writeln;
  26. writeln('i_32u = ',IntToBin(i_32u,32),' ',i_32u);
  27. write('i_32u << 1 = ');
  28. try i_32u_shift:= (i_32u << 1); except; end;
  29. writeln(IntToBin(i_32u_shift,32),' ',i_32u_shift);
  30. writeln;
  31. end;
  32.  
  33. begin
  34. i_16u:= 65535;
  35. i_32u:= 4294967295;
  36. shift_1;
  37. i_16u:= 1;
  38. i_32u:= 1;
  39. shift_1;
  40. end.
  41.  
What happens is:
With range-checking enabled, and only for 16-bit and 8-bit unsigned integers, and only with a value of hex FFFF or FF, a shl (<<) operation appears to do nothing except trash/zero the most significant bit of the result. No shift takes place, because the least significant bit remains zero. For 8-bit and 16-bit unsigned integers with other values, a shl (<<) operation works as expected, even with range-checking enabled.

With range-checking disabled, everything works as expected and consistently.
For 32-bit and 64bit unsigned integers, everything works as expected and consistently.

I'm not sure whether this behaviour this due to the hardware or the compiled code, because I have no knowledge of Intel assembly language.
« Last Edit: March 05, 2024, 06:20:00 pm by ad1mt »

domasz

  • Sr. Member
  • ****
  • Posts: 435
Re: Inconsistent behaviour with bitshift operations
« Reply #1 on: March 05, 2024, 01:38:28 pm »
What is this unholy nonsense? << and >> ? shl and shr!

runewalsh

  • Jr. Member
  • **
  • Posts: 82
Re: Inconsistent behaviour with bitshift operations
« Reply #2 on: March 05, 2024, 01:47:39 pm »
Well, you are suppressing the exception from the range check, I guess that the variable you were assigning to becomes undefined (instead of simply unchanged).

TRon

  • Hero Member
  • *****
  • Posts: 2534
Re: Inconsistent behaviour with bitshift operations
« Reply #3 on: March 05, 2024, 02:13:11 pm »
@ad1mt: Are you aware these exists (don't forget to look/scroll down) ?

In case you do not like that, then create your own type that adheres to your bidding.

Thaddy

  • Hero Member
  • *****
  • Posts: 14377
  • Sensorship about opinions does not belong here.
Re: Inconsistent behaviour with bitshift operations
« Reply #4 on: March 05, 2024, 02:45:19 pm »
shifts should not be range checked because the range is defined.
but the rangecheck error should not occur because of the above so that is a bug.
plz report it.
the bug also occurs with the string helpers form sysutils instead of using strutils.
« Last Edit: March 05, 2024, 02:58:18 pm by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

TRon

  • Hero Member
  • *****
  • Posts: 2534
Re: Inconsistent behaviour with bitshift operations
« Reply #5 on: March 05, 2024, 03:23:22 pm »
shifts should not be range checked because the range is defined.

no, it isn't (or actually it is but it depends on the platform)

Code: Pascal  [Select][+][-]
  1. writeln(sizeof(i_16u >> 1));
  2. writeln(sizeof(i_32u >> 1));
  3. writeln(sizeof(i_16u << 1));
  4. writeln(sizeof(i_32u << 1));
  5.  
All this because someone was not able to apply a proper and... it is documented.

jamie

  • Hero Member
  • *****
  • Posts: 6128
Re: Inconsistent behaviour with bitshift operations
« Reply #6 on: March 05, 2024, 11:45:27 pm »
TypeCast Type Cast ! that fixes everything, just like a hammer.! :(
The only true wisdom is knowing you know nothing

TRon

  • Hero Member
  • *****
  • Posts: 2534
Re: Inconsistent behaviour with bitshift operations
« Reply #7 on: March 06, 2024, 07:17:51 pm »
An even better approach jamie  :)

ad1mt

  • Full Member
  • ***
  • Posts: 199
    • Mark Taylor's Home Page
Re: Inconsistent behaviour with bitshift operations
« Reply #8 on: March 08, 2024, 09:33:53 am »
Probably best to close this... I can not reproduce the problem consistently.

TRon

  • Hero Member
  • *****
  • Posts: 2534
Re: Inconsistent behaviour with bitshift operations
« Reply #9 on: March 08, 2024, 09:41:39 am »
Probably best to close this... I can not reproduce the problem consistently.
You might want to checkout this from the programmer's guide together with this (be sure to read about type conversion) as well as this to get a better understanding as why some platforms/different sized integers are treated differently. Mix them together and you can get lost pretty quick.
« Last Edit: March 08, 2024, 09:45:31 am by TRon »

ad1mt

  • Full Member
  • ***
  • Posts: 199
    • Mark Taylor's Home Page
Re: Inconsistent behaviour with bitshift operations
« Reply #10 on: March 09, 2024, 01:27:23 pm »
The problems I've encountered are inconsistencies in behaviour, depending on whether range-checking was switched on, and how it was switched on. These are my latest tests that reproduce the inconsistency in a consistent way ( :D ):
This code works as I would expect:
Code: Pascal  [Select][+][-]
  1. program bitshift_demo_4C;
  2. uses strutils;
  3. var
  4. v_int16,
  5. v_int16_shift   :int16;
  6. begin
  7. writeln('{$R-}');
  8. {$R-}
  9. v_int16:= 32767;
  10. writeln('v_int16 = ',IntToBin(v_int16,16),' ',v_int16);
  11. write('v_int16 shl 1 = ');
  12. try v_int16_shift:= (v_int16 shl 1); except; end;
  13. writeln(IntToBin(v_int16_shift,16),' ',v_int16_shift);
  14. writeln;
  15. writeln('{$R+}');
  16. {$R+}
  17. v_int16:= 32767;
  18. writeln('v_int16 = ',IntToBin(v_int16,16),' ',v_int16);
  19. write('v_int16 shl 1 = ');
  20. try v_int16_shift:= (v_int16 shl 1); except; end;
  21. writeln(IntToBin(v_int16_shift,16),' ',v_int16_shift);
  22. writeln;
  23. end.
The output from the above code is:
Code: Text  [Select][+][-]
  1. {$R-}
  2. v_int16 = 0111111111111111 32767
  3. v_int16 shl 1 = 1111111111111110 -2
  4.  
  5. {$R+}
  6. v_int16 = 0111111111111111 32767
  7. v_int16 shl 1 = 1111111111111110 -2
However, this code shows the problem, as I see it:
Code: Pascal  [Select][+][-]
  1. program bitshift_demo_4D;
  2. uses strutils;
  3. var
  4. v_int16,
  5. v_int16_shift   :int16;
  6. begin
  7. writeln('{$R+}');
  8. {$R+}
  9. v_int16:= 32767;
  10. writeln('v_int16 = ',IntToBin(v_int16,16),' ',v_int16);
  11. write('v_int16 shl 1 = ');
  12. try v_int16_shift:= (v_int16 shl 1); except; end;
  13. writeln(IntToBin(v_int16_shift,16),' ',v_int16_shift);
  14. writeln;
  15. writeln('{$R-}');
  16. {$R-}
  17. v_int16:= 32767;
  18. writeln('v_int16 = ',IntToBin(v_int16,16),' ',v_int16);
  19. write('v_int16 shl 1 = ');
  20. try v_int16_shift:= (v_int16 shl 1); except; end;
  21. writeln(IntToBin(v_int16_shift,16),' ',v_int16_shift);
  22. writeln;
  23. end.
The output from the second test is:
Code: Text  [Select][+][-]
  1. {$R+}
  2. v_int16 = 0111111111111111 32767
  3. v_int16 shl 1 = 0000000000000000 0
  4.  
  5. {$R-}
  6. v_int16 = 0111111111111111 32767
  7. v_int16 shl 1 = 1111111111111110 -2
In the first test, the behaviour of the left shift is correct, when range-checking is first switched off and then switched on. In the second test, the behaviour of the left shift is different, when range-checking is first switched on and then switched off. At the point where the behaviour become inconsistent, the state of range-checking is the same, i.e. switched on.

One of these cases has to be incorrect, they cannot both be correct. If you are trying to write robust code, this is a big problem.

But it also indicates a bug, which might have other side-effects that I have not yet discovered.
« Last Edit: March 10, 2024, 11:26:32 am by ad1mt »

jamie

  • Hero Member
  • *****
  • Posts: 6128
Re: Inconsistent behaviour with bitshift operations
« Reply #11 on: March 09, 2024, 07:32:36 pm »
I suspect the compiler is free to do as it pleases with a value that just faulted.

most likely the range check is done beforehand, and the resulting register used, is never completed in its transaction, because it faulted.

if you are trying to use the exception system to determine a sign bit shift then maybe you are going about it the wrong way?
The only true wisdom is knowing you know nothing

Thaddy

  • Hero Member
  • *****
  • Posts: 14377
  • Sensorship about opinions does not belong here.
Re: Inconsistent behaviour with bitshift operations
« Reply #12 on: March 09, 2024, 07:49:38 pm »
 SarShortint, SarSmallint, SarLongint, SarInt64 will preserve sign
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

 

TinyPortal © 2005-2018