C#按位向左旋转和向右旋转 < > >代码> > RoLL和>ROTR C++中的位旋转的内置语言特征是什么?但是这些扩展方法应该做的工作是: public static uint RotateLeft(this uint value, int count) { return (value << count) | (value >> (32 - count)) } public static uint RotateRight(this uint value, int count) { return (value >> count) | (value << (32 - count)) }
(请注意,C#按位向左旋转和向右旋转 < > >代码> > RoLL和>ROTR C++中的位旋转的内置语言特征是什么?但是这些扩展方法应该做的工作是: public static uint RotateLeft(this uint value, int count) { return (value << count) | (value >> (32 - count)) } public static uint RotateRight(this uint value, int count) { return (value >> count) | (value << (32 - count)) },c#,bitwise-operators,C#,Bitwise Operators,(请注意,int.MinValue是111111111111-32 1s的二进制格式。)这就是您想要做的吗 基本上你想要的是 (左) (原始>(32位)) 或 (右图) (原始>>位)|(原始原始版本的移位不起作用。原因是,右移位有符号数将用符号位填充左位,而不是0: 您可以通过以下方式验证此事实: Console.WriteLine(-1 >> 1); 正确的方法是: public static int RotateLeft(this int value, int count
int.MinValue
是111111111111-32 1s的二进制格式。)这就是您想要做的吗
基本上你想要的是
(左)
(原始>(32位))
或
(右图)
(原始>>位)|(原始原始版本的移位不起作用。原因是,右移位有符号数将用符号位填充左位,而不是0:
您可以通过以下方式验证此事实:
Console.WriteLine(-1 >> 1);
正确的方法是:
public static int RotateLeft(this int value, int count)
{
uint val = (uint)value;
return (int)((val << count) | (val >> (32 - count)));
}
public static int RotateRight(this int value, int count)
{
uint val = (uint)value;
return (int)((val >> count) | (val << (32 - count)));
}
public static int RotateLeft(此int值,int计数)
{
uint val=(uint)值;
返回值(int)((val>(32-计数));
}
公共静态int RotateRight(此int值,int计数)
{
uint val=(uint)值;
return(int)((val>>count)|(val注意,如果要创建对较短整数值进行操作的重载,则需要添加额外的步骤,如所示:
public static byte RotateLeft(
this byte value,
int count )
{
// Unlike the RotateLeft( uint, int ) and RotateLeft( ulong, int )
// overloads, we need to mask out the required bits of count
// manually, as the shift operaters will promote a byte to uint,
// and will not mask out the correct number of count bits.
count &= 0x07;
return (byte)((value << count) | (value >> (8 - count)));
}
公共静态字节RotateLeft(
这个字节值,
整数计数)
{
//与RotateLeft(uint,int)和RotateLeft(ulong,int)不同
//重载时,我们需要屏蔽所需的计数位
//手动,因为移位运算符将一个字节提升到uint,
//并且不会掩盖正确的计数位数。
计数&=0x07;
返回(字节)((值>8-计数));
}
32位和64位重载不需要掩蔽操作,因为移位运算符自己负责左手操作数大小的掩蔽操作。使用最新的C#7,您现在可以通过ref扩展方法创建,这样您就可以摆脱不断存储将helper函数返回到变量中
// if you are using string
string str=Convert.ToString(number,2);
str=str.PadLeft(32,'0');
//Rotate right
str = str.PadLeft(33, str[str.Length - 1]);
str= str.Remove(str.Length - 1);
number=Convert.ToInt32(str,2);
//Rotate left
str = str.PadRight(33, str[0]);
str= str.Remove(0,1);
number=Convert.ToInt32(str,2);
这很好地简化了rotate函数,消除了一类常见的错误,即您忘记重新存储函数的返回值,但同时可能引入了一种全新的、完全不同的错误类型,即ValueTypes
在您不希望或不期望的情况下,不经意地在原地修改
public static void Rol(ref this ulong ul) => ul = (ul << 1) | (ul >> 63);
public static void Rol(ref this ulong ul, int N) => ul = (ul << N) | (ul >> (64 - N));
public static void Ror(ref this ulong ul) => ul = (ul << 63) | (ul >> 1);
public static void Ror(ref this ulong ul, int N) => ul = (ul << (64 - N)) | (ul >> N);
/// note: ---^ ^---^--- extension method can now use 'ref' for ByRef semantics
这是每个对应呼叫站点的C代码。由于完全JIT优化的AMD64代码非常小,我也可以将其包括在这里。这是最佳情况:
static ulong x = 1; // static so it won't be optimized away in this simple test
// ------- ByVal extension method; c̲a̲l̲l̲e̲r̲ must reassign 'x' with the result -------
x = x.Rol_ByVal();
// 00007FF969CC0481 mov rax,qword ptr [7FF969BA4888h]
// 00007FF969CC0487 rol rax,1
// 00007FF969CC048A mov qword ptr [7FF969BA4888h],rax
// ------- New in C#7, ByRef extension method can directly alter 'x' in-situ -------
x.Rol_ByRef();
// 00007FF969CC0491 rol qword ptr [7FF969BA4888h],1
哇。是的,这可不是开玩笑。一下子我们就可以看出,ECMA CIL(.NET)中间语言中明显缺乏操作码.Rot
-指令家族几乎没有问题;抖动能够看穿我们的一堆C#变通代码(ul>63)
预测其基本意图,在这两种情况下,x64 JIT都通过简单地发出本机rol
指令来实现。令人印象深刻的是,ByRef版本使用一条指令直接在主存目标地址上执行旋转,甚至不将其加载到寄存器中
在ByVal的情况下,您仍然可以看到多余复制的残留痕迹,这是在被调用方法完全优化之前保持调用方原始值不变所必需的(这是值类型语义的本质)。对于此处的整数旋转,它只是将目标整数额外提取/存储到64位寄存器rax
为了澄清这一点,让我们在调试会话中重新抑制JIT优化。这样做将使我们的助手扩展方法返回,并提供完整的正文和堆栈框架来更好地解释上一段的第一句话。首先,让我们看看调用站点。这里我们可以看到传统的ValueType
semanti的效果cs,这归结为确保没有较低的堆栈帧可以操作任何父帧的ValueType
副本:
按值:
x = x.Rol_ByVal();
// 00007FF969CE049C mov rcx,qword ptr [7FF969BC4888h]
// 00007FF969CE04A3 call 00007FF969CE00A8
// 00007FF969CE04A8 mov qword ptr [rbp-8],rax
// 00007FF969CE04AC mov rcx,qword ptr [rbp-8]
// 00007FF969CE04B0 mov qword ptr [7FF969BC4888h],rcx
x.Rol_ByRef();
// 00007FF969CE04B7 mov rcx,7FF969BC4888h
// 00007FF969CE04C1 call 00007FF969CE00B0
// ...all done, nothing to do here; the callee did everything in-place for us
参考
x = x.Rol_ByVal();
// 00007FF969CE049C mov rcx,qword ptr [7FF969BC4888h]
// 00007FF969CE04A3 call 00007FF969CE00A8
// 00007FF969CE04A8 mov qword ptr [rbp-8],rax
// 00007FF969CE04AC mov rcx,qword ptr [rbp-8]
// 00007FF969CE04B0 mov qword ptr [7FF969BC4888h],rcx
x.Rol_ByRef();
// 00007FF969CE04B7 mov rcx,7FF969BC4888h
// 00007FF969CE04C1 call 00007FF969CE00B0
// ...all done, nothing to do here; the callee did everything in-place for us
正如我们从与这两个片段相关联的C#代码中所期望的那样,我们看到by val调用方在调用返回后有一系列工作要做。这是一个用完全独立的ulong
值覆盖ulong
值“x”的父副本的过程在rax
寄存器中返回
现在让我们看看调用的目标函数的代码。查看它们需要强制JIT“抑制”优化。以下是x64版本JIT为Rol\u ByVal
和Rol\u ByRef
函数发出的本机代码
为了关注二者之间微小但至关重要的区别,我剥离了一些管理样板文件(我将堆栈框架设置和拆卸留给了上下文,并在本例中展示了如何在实际内容指令的基础上增加辅助内容)你能看到ByRef在工作中的间接作用吗?我指出了这一点很有帮助:-/
static ulong Rol_ByVal(this ulong ul) => (ul << 1) | (ul >> 63);
// 00007FF969CD0760 push rbp
// 00007FF969CD0761 sub rsp,20h
// 00007FF969CD0765 lea rbp,[rsp+20h]
// ...
// 00007FF969CE0E4C mov rax,qword ptr [rbp+10h]
// 00007FF969CE0E50 rol rax,1
// 00007FF969CD0798 lea rsp,[rbp]
// 00007FF969CD079C pop rbp
// 00007FF969CD079D ret
static void Rol_ByRef(ref this ulong ul) => ul = (ul << 1) | (ul >> 63);
// 00007FF969CD0760 push rbp
// 00007FF969CD0761 sub rsp,20h
// 00007FF969CD0765 lea rbp,[rsp+20h]
// ...
// 00007FF969CE0E8C mov rax,qword ptr [rbp+10h]
// 00007FF969CE0E90 rol qword ptr [rax],1 <--- !
// 00007FF969CD0798 lea rsp,[rbp]
// 00007FF969CD079C pop rbp
// 00007FF969CD079D ret
静态ulong Rol_ByVal(此ulong ul)=>(ul>63);
//00007FF969CD0760推送rbp
//00007FF969CD0761子rsp,20小时
//00007FF969CD0765 lea rbp,[rsp+20小时]
// ...
//00007FF969CE0E4C mov rax,qword ptr[rbp+10h]
//00007FF969CE0E50 rol rax,1
//00007FF969CD0798 lea rsp,[rbp]
//00007FF969CD079C pop rbp
//00007FF969CD079D ret
静态无效Rol_ByRef(参考此ulong ul)=>ul=(ul>63);
//00007FF969CD0760推送rbp
//00007FF969CD0761子rsp,20小时
//00007FF969CD0765 lea rbp,[rsp+20小时]
// ...
//00007FF969CE0E8C mov rax,qword ptr[rbp+10h]
//00007FF969CE0E90 rol qword ptr[rax],1对于使用.NET Core 3.0或.NET 5.0及更高版本的用户,可以使用和。@Mehrdad:是的,我刚意识到这一点。修复了否
x.Rol_ByRef();
// 00007FF969CE04B7 mov rcx,7FF969BC4888h
// 00007FF969CE04C1 call 00007FF969CE00B0
// ...all done, nothing to do here; the callee did everything in-place for us
static ulong Rol_ByVal(this ulong ul) => (ul << 1) | (ul >> 63);
// 00007FF969CD0760 push rbp
// 00007FF969CD0761 sub rsp,20h
// 00007FF969CD0765 lea rbp,[rsp+20h]
// ...
// 00007FF969CE0E4C mov rax,qword ptr [rbp+10h]
// 00007FF969CE0E50 rol rax,1
// 00007FF969CD0798 lea rsp,[rbp]
// 00007FF969CD079C pop rbp
// 00007FF969CD079D ret
static void Rol_ByRef(ref this ulong ul) => ul = (ul << 1) | (ul >> 63);
// 00007FF969CD0760 push rbp
// 00007FF969CD0761 sub rsp,20h
// 00007FF969CD0765 lea rbp,[rsp+20h]
// ...
// 00007FF969CE0E8C mov rax,qword ptr [rbp+10h]
// 00007FF969CE0E90 rol qword ptr [rax],1 <--- !
// 00007FF969CD0798 lea rsp,[rbp]
// 00007FF969CD079C pop rbp
// 00007FF969CD079D ret