What is the C# equivalent (.NET 2.0) of _rotl
and _rotr
from C++?
相关问题
- Sorting 3 numbers without branching [closed]
- Graphics.DrawImage() - Throws out of memory except
- Why am I getting UnauthorizedAccessException on th
- 求获取指定qq 资料的方法
- How to know full paths to DLL's from .csproj f
There's no built-in language feature for bit rotation in C#, but these extension methods should do the job:
Note: As Mehrdad points out, right-shift (
>>
) for signed integers is a peculiarity: it fills the MSBs with sign bit rather than 0 as it does for unsigned numbers. I've now changed the methods to take and returnuint
(unsigned 32-bit integer) instead - this is also in greater accordance with the C++rotl
androtr
functions. If you want to rotate integers, just case them before passing, and again cast the return value, of course.Example usage:
(Note that
int.MinValue
is 111111111111111111111111 - 32 1s in binary.)The naive version of shifting won't work. The reason is, right shifting signed numbers will fill the left bits with sign bit, not 0:
You can verify this fact with:
The correct way is:
With the latest C# 7, you can now create by-ref extension methods, so you can get rid of the busywork of constantly storing the return value from the helper function back into the variable.
This streamlines the rotate functions nicely, and eliminates a common class of bug where you forget to re-store the function's return value, yet while possibly introducing a new, completely different type of bug--where
ValueTypes
are inadvertently getting modified in-situ when you didn't want or expect them to be.Usually I would be sure to put
[MethodImpl(MethodImplOptions.AggressiveInlining)]
on small methods like these, but after some investigation (on x64) I found out that it's not necessary at all here. If the JIT determines the method is eligible (for example, if you uncheck the VisualStudio debugger checkbox 'Suppress JIT Optimization', which is enabled by default) the methods will inlined regardless, and that is the case here.To demonstrate the use of a by-ref extension method, I'll focus just on the first method shown above "rotate left", and compare the JIT output between the traditional by-value extension method and the newer by-ref approach. Here are the two test methods to be compared on x64 Release in .NET 4.7 on Windows 10. As noted above, this will be with JIT optimization 'not-suppressed', so under these test conditions as you'll see, the functions will completely disappear into inline code.
And here is the C# code for each corresponding call site. Since the fully JIT-optimized AMD64 code is so small, I can just include it here as well. This is the optimal case:
Wow. Yes, that's no joke. Right off the bat we can see that the glaring lack of an
OpCodes.Rot
-family of instructions in the ECMA CIL (.NET) intermediate language is pretty much of a non-issue; The jitter was able to see through our pile of C# workaround code(ul << 1) | (ul >> 63)
to divine its essential intent, which in both cases the x64 JIT implements by simply emitting a nativerol
instruction. Impressively, the ByRef version uses a single instruction to perform the rotation directly on the main-memory target address without even loading it into a register.In the ByVal case, you can still see a residual trace of the excess copying which was necessary to leave the caller's original value unchanged, prior to the called method being entirely optimized-away (as is the essence of value-type semantics). For integer rotate here, it's just an extra fetch/store of the target integer into a 64-bit register
rax
.To clarify that, let's re-suppress JIT optimizations in the debugging session. Doing so will make our helper extension methods come back, with full bodies and stack frames to better explain the first sentence of the preceding paragraph. First, let's look at the call sites. Here we can see the effect of traditional
ValueType
semantics, which boils down to ensuring that no lower stack frame can manipulate any parent frame'sValueType
copies:by-value:
by-reference
As we might expect from the C# code associated with each of these two fragments, we see that the by-val caller has a bunch of work to do after the call returns. This is the process of overwriting the parent copy of the
ulong
value 'x' with the completely independentulong
value that's returned in therax
register.Now let's look at the code for the called target functions. Seeing them requires forcing the JIT to "suppress" the optimizations. The following is the native code the x64 Release JIT emits for
Rol_ByVal
andRol_ByRef
functions.In order to focus on the tiny but crucial difference between the two, I've stripped away some of administrative boilerplate. (I left the stack frame setup and teardown for context, and to show how in this example, that ancillary stuff pretty much dwarfs the actual contentful instructions.) Can you see the ByRef's indirection at work? Well, it helps that I pointed it out :-/
You might notice that both calls are in fact passing the parent's instance of the
ulong
value by reference--both examples are identical in this regard. The parent indicates the address where its private copy oful
resides in the upper stack frame. Turns out it's not necessary to insulate callees from reading those instances where they lie, as long as we can be sure they never write to those pointers. This is a "lazy" or deferred approach which assigns to each lower (child) stack frame the responsibility for preserving the ValueType semantics of its higher-up callers. There's no need for a caller to proactively copy anyValueType
passed down to a child frame if the child never ends up overwriting it; to maximize the avoidance of unnecessary copying as much as possible, only the child can make the latest-possible determination.Also interesting is that we might have an explanation here for the clunky use of
rax
in the first 'ByVal' example I showed. After the by-value method had been completely reduced via inlining, why did the rotation still need to happen in a register?Well in these latest two full-method-body versions its clear that the first method returns
ulong
and the second isvoid
. Since a return value is passed inrax
, the ByVal method here has to fetch it into that register anyway, so it's a no-brainer to rotate it there too. Because the ByRef method doesn't need to return any value, it doesn't need to stick anything for its caller anywhere, let alone inrax
. It seems likely that "not having to bother withrax
" liberates the ByRef code to target theulong
instance its parent has shared 'where it lies', using the fancyqword ptr
to indirect into the parent's stack frame memory, instead of using a register. So that's my speculative, but perhaps credible, explanation for the "residualrax
" mystery we saw earlier.Is this what you are trying to do?
Jon Skeet answered this in another site
Basically what you want is
(for left)
or
(for right)
Also, as Mehrdad has already suggested, this only works for uint, which is the example that Jon gives as well.
Note that if you want to create overloads that operate on shorter integral values, you need to add an extra step, as shown in:
The masking operation is not needed for the 32-bit and 64-bit overloads, as the shift operators themselves take care of it for those sizes of left-hand operands.