#1044 Logical Right Shift

katox Sun 28 Mar 2010

Fantom doesn't have bitwise operators, instead normal method calls are used - this is kind of confusing statement as in Fantom right logical shift ( >>> ) is implemented as right artihmetic shift ( >> ).

I think there should also be a method for unsigned logical right shift, something like shiftru.

Alternatively, it might be even better to default to unsigned shifts as most people probably use this operation for bitmasks not for fast signed division. Then the methods would be someting like shiftr and shiftrs.

EDIT: removed clr note

KevinKelley Sun 28 Mar 2010

(in CLR) the second parameter is masked so there could be different results

Isn't that what the JLS says, too? That's how it works:

C:\Documents and Settings\Kevin\Desktop>fansh
Fantom Shell v1.0.51.1 ('?' for help)
fansh> 15.shiftr(1)
7
fansh> 15.shiftr(65)
7

Anyway, I don't have a strong opinion on whether it goes in. Here's a little test for how I think it should work, if it does...

class lsr
{
  Int shiftru(Int x, Int n) {
    if (n <= 0) return x.shiftr(n) 
    if (n >= 64) return 0
    return x.shiftr(n).and(1.shiftl(64 - n) - 1)
  }
  Void main()
  {
    (0..2).each |i| {
      echo("shiftru $i")
      (-4..4).each |j| {
        echo(shiftru(j, i).toHex(16))
      }
    }
  }
}

Actually, that brings up an odd behavior for negative-shifts: apparently doing a right-shift by a negative amount, yields just the sign bit, fully extended. Wonder if that's documented anywhere? :-)

katox Sun 28 Mar 2010

@KevinKelly You're right about JLS.

JLS Shift Operators says

The value of n>>>s is n right-shifted s bit positions with zero-extension. If n is positive, then the result is the same as that of n>>s; if n is negative, the result is equal to that of the expression (n>>s)+(2<<~s) if the type of the left-hand operand is int, and to the result of the expression (n>>s)+(2L<<~s) if the type of the left-hand operand is long. The added term (2<<~s) or (2L<<~s) cancels out the propagated sign bit. (Note that, because of the implicit masking of the right-hand operand of a shift operator, ~s as a shift distance is equivalent to 31-s when shifting an int value and to 63-s when shifting a long value.)

So I'd say it is safe to map shiftru to >>> in Java/Javascript (though JS only supports 31 bit precision, if I recall correctly) and use this spec for C#.

brian Sun 28 Mar 2010

I've done a ton of bit-level twiddling in Java, and I've never really cared about the distinction. Although that might be because Java makes it so painful to work with bytes you can up masking everything with 0xff.

My suggestion is to just make shiftr be unsigned and use >>> and be done with it, unless someone sees value in having both.

qualidafial Sun 28 Mar 2010

What about making it a flag argument e.g. rshift(Int shift, Bool signed = true)?

KevinKelley Sun 28 Mar 2010

I spent some time, a while back, doing some nifty bitops optimizations, and in the course of testing I finally noticed that when I cranked the iterations over 100,000 all of a sudden all my different versions took exactly the same amount of time. Punchline: Hotspot.

So I don't know if the old reasons for having the various operators, apply any more. Things are different when you're living on a VM than when you need to get to the CPU.

If we have signed-shift, and you need unsigned, then shift-and-mask works with no real penalty. If we have unsigned-shift, and you need signed, I guess it's a little more verbose, but I agree that this is the less-used variety, so that's probably fine.

(edit) Actually I like qualidafials suggestion...

katox Sun 28 Mar 2010

What about making it a flag argument

I don't think it is really worth the performance hit. Personally, I am ok with just changing >> to >>> because an arithmetic shift can be more easily substituted with a division.

brian Mon 29 Mar 2010

I don't really want to make use of a flag because this method is optimized into a single instruction on the JVM.

I changed shiftr to be a unsigned based shift. That seems like the best default behavior.

We can always go back and add another method for signed shift, but I personally don't think it is really useful.

changeset

katox Mon 29 Mar 2010

Thanks. Not sure if js implementation would change anything but at least for clarity this should probably go in too:

diff --git a/src/sys/js/fan/Int.js b/src/sys/js/fan/Int.js
index c919e74..9bdbda9 100644
--- a/src/sys/js/fan/Int.js
+++ b/src/sys/js/fan/Int.js
@@ -188,7 +188,7 @@ fan.sys.Int.and = function(a, b) { var x = a & b;  if (x<0) x += 0xffffffff+1; r
 fan.sys.Int.or  = function(a, b) { var x = a | b;  if (x<0) x += 0xffffffff+1; return x; }
 fan.sys.Int.xor = function(a, b) { var x = a ^ b;  if (x<0) x += 0xffffffff+1; return x; }
 fan.sys.Int.shiftl = function(a, b) { var x = a << b; if (x<0) x += 0xffffffff+1; return x; }
-fan.sys.Int.shiftr = function(a, b) { var x = a >> b; if (x<0) x += 0xffffffff+1; return x; }
+fan.sys.Int.shiftr = function(a, b) { var x = a >>> b; if (x<0) x += 0xffffffff+1;   return x; }

 //////////////////////////////////////////////////////////////////////////
 // Conversion

Login or Signup to reply.