Bitwise C# vs Elixir

erlang

#1

Hi working on some bitwise operations based on C# but end with some estrange results in elixir…

On C#

ulong c1 = 9223372036878134386;
ulong c4 = 0;
var d1_c1_a = c1 << 1; // 46717156
var d1_c1_b = c1 >> -1; // 1
var d1_c1_ab = d1_c1_a | d1_c1_b; // 46717157
var d1_c1_c = d1_c1_ab ^ c4; // 46717157

On Elixir

c1 = 9223372036878134386
c4 = 0
d1_c1_a = (c1 <<< 1) &&& 0xFFFFFFFFFFFFFFFF # 46717156
d1_c1_b = c1 >>> -1 # 18446744073756268772
d1_c1_b2 = c1 >>> -1  &&& 0xFFFFFFFFFFFFFFFF # 46717156
d1_c1_ab = d1_c1_a ||| d1_c1_b # 18446744073756268772
d1_c1_c = (d1_c1_ab ^^^ c4) # 18446744073756268772

Any idea of why C# returns 1 when c1 >> -1 and elixir not?


#2

Haven’t done anything to verify this, but I suspect it’s because your type in C# ulong has a particular size and so doing that operation causes it to overflow. Whereas, the type in Elixir would be equivalent to a BigInteger in C#, which doesn’t have that problem.


#3

Yea, thats normal… then i end for the <<< 1 with 0xFF…FFF


#5

This appears to be the answer: https://stackoverflow.com/questions/1881455/why-am-i-getting-strange-results-bit-shifting-by-a-negative-value

If the first operand is a long or ulong (64-bit quantity), the shift count is given by the low-order six bits of the second operand. That is, the actual shift count is 0 to 63 bits.


#6

Well… then i get confused, on sharp the complete expression is

ulong d1 = (c1 << 1 | c1 >> -1) ^ c4; // 46717157

need figure out how to translate >> -1 to elixir… mmm…


#7

Ok i got it, im looking the docs of << not >>

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/right-shift-operator

-1 for C# = 0x3f


#8

I’m curious how that would work…

Bitshifting operates on non-negative numbers, a -1 would become an integer with all bits set to 1, and since Elixir’s integers are ‘BigInteger’ as known in .NET then its size depends on how much is allocated within it, which will always be quite a good bit larger, which we can see when we serialize it out:

iex(1)> <<131, 98, int::binary>> = :erlang.term_to_binary(-1)
<<131, 98, 255, 255, 255, 255>>
iex(2)> int
<<255, 255, 255, 255>>

C/C++/OCaml/etc and any other language I’ve used works the same way, and thus shifting by such a huge number will be… very language dependency and not well defined. ^.^;
Such as in C/C++ shifting by a negative number, say -5, is undefined, however it will actually end up performing a modulo 32 on it to become 27 due to the way it encodes the instruction in hardware in ‘many’ situations (for a 32 bit integer that is, this is because they generate a SAL opcode in assembly that just uses the bottom 5 bits in hardware).

In general though, shifting is only well defined on unsigned integers. Using unsigned integers will either be mal-defined, undefined, or some language will come up with something arbitrary. I think it’s technically an undefined operation on the BEAM so it can do anything it wants, same as in C/C++/Java/etc…

This is of course all for shifting by a signed number. Shifting a signed number itself (left side) is of course fine, though that can be different depending on if you are doing an algebraic shifting or a bitwise shifting (C/C++ change based on the type of the operand, Java has different operators, etc…).


#9

I think the BEAM is defining shift right by a negative as left shifting by that amount and vice versa.

use Bitwise
1 >>> -2 == 1 <<< 2
1 <<< -2 == 1 >>> 2

#10

Implementation defined it seems though. I couldn’t find where that’s stated in the specs/docs. Can anyone else find out for sure?


#11

It might be easier to use Bit length pattern matching