如何在C#中对整数的特定字节执行-1操作?

5

我有一个整数u=101057541。

转换成二进制,它等于:00000110 00000110 00000100 00000101

现在,我将每个字节视为单独的十进制数(因此在这种情况下为6、6、4、5)。 我想从第一个字节中减去-1,结果为6-1=5。 我尝试按如下方式操作:

int West = u | (((u>>24) - 1) << 24);

然而,当我对这个字节 1 时,结果与减去 -1 时相同。有人能解释一下原因并告诉我如何从这个字节中减去-1吗?

更新: 所以,我想要的结果是以下二进制数:

00000101 00000110 00000100 00000101


你减去了-1,好的。 - PepitoSh
如果你有u = 1; 它将是 00000000 00000000 00000000 00000001 => 0 0 0 1,你想在这里做什么,0-1还是1-1? - Amit
我想知道在SO上谁会给这样的问题点赞。 - PepitoSh
感谢大家的快速回复。现在我明白了我的错误,使用掩码获得了正确的结果。 - Rutger Mauritz
2个回答

4
由于你正在将该字节进行“或”操作,因此:
 u | (((u>>24) - 1) << 24);

应该是

 (u & mask) | (((u>>24) - 1) << 24);

其中mask表示不包含正在处理的字节的所有内容。

你可能会发现使用不安全代码更容易:

int i = 101057541;
byte* b = (byte*)&i;
b[3]--; // note CPU endianness is important here
Console.WriteLine(i);

如果你正在使用最新的程序包,你可以使用 "spans" 来避免使用 unsafe 的方法,实现相同的功能。

int i = 101057541;
var bytes = MemoryMarshal.Cast<int, byte>(MemoryMarshal.CreateSpan(ref i, 1));
bytes[3]--; // note CPU endianness is important here
Console.WriteLine(i);

或者您可以通过使用具有显式布局的struct来使用“union”,因此4个字节重叠1个int:

var x = new Int32Bytes();
x.Value = 101057541;
x.Byte3--; // note CPU endianness is important here
Console.WriteLine(x.Value);

使用:

[StructLayout(LayoutKind.Explicit)]
struct Int32Bytes
{
    [FieldOffset(0)]
    public int Value;
    [FieldOffset(0)]
    public byte Byte0;
    [FieldOffset(1)]
    public byte Byte1;
    [FieldOffset(2)]
    public byte Byte2;
    [FieldOffset(3)]
    public byte Byte3;
}

我确实在0x00FFFFFF中进行了更改。但是,现在我想对我的整数的第二个字节做同样的事情,为此我尝试使用0xFF00FFFF。然而,在这种情况下,我收到错误消息“无法将类型'long'转换为'int'”。你知道为什么会发生这种情况吗?(我对C#非常陌生) - Rutger Mauritz
1
@RutgerMauritz 编译器对于最后一位的常量有些敏感 - 使用 ~0xFF0000 或者 ~(0xFF << 16) 可能更容易,但这也说明了其他方法为什么会更简单... - Marc Gravell
非常感谢。虽然这种掩码对我很有效,但我需要为我的研究构建一个特定的程序,他们非常注重时间效率。我必须在某个while循环中使用这种字节算术,那么使用掩码难道不是最有效的方法吗? - Rutger Mauritz
1
@RutgerMauritz 不一定,你会惊讶于 netcoreapp2.1 中 span 代码的效率有多高 - JIT 的工作量非常大,使其变得非常快;而 unsafe 一直都很快 - 只是比较丑陋。请记住,尽管 unsafeSpan* 代码看起来可能很复杂,在 JIT 过程中,其中大部分实际上都会消失不见 - 因此,它基本上只是一个原地引用计数器减少操作 - 这比移位和掩码处理要更简单。对于性能的真正答案,建议使用像 benchmarkdotnet 这样的工具(它是免费的等)。 - Marc Gravell
@RutgerMauritz 如果不使用benchmarkdotnet,我很难说出这些哪个更“快”:https://sharplab.io/#v2:EYLgtghgzgLgpgJwDQxNMAfAAgZgARYBMeAwgLABQA3pXnQQIwBseArgHZQQBmceAluxh4AInADGCAKqcecABSDhrAJS16NCvW14AbhAR5gAT3hQ8AXjwAyVgG51Oo6bhQA2jgC6AWm8OtTgQA7Gz+2gC+jowsSqISCADKABb83DCKQmxqAXSagcF08qw2eAAMAB6lpQBitXXZ+doYePKtrAB87YQALCp43ngMfQA8w3g9KmH0kRThQA - Marc Gravell
显示剩余2条评论

1
当你从00000110中减去1时,结果为00000101。你将其与原始值进行OR运算,得到0000111,这就像加了1一样。
作为解决问题的一行代码,你应该屏蔽你正在操作的位的区域:
int West = (u & 0x00FFFFFF) | ((((u & 0xFF000000)>>24) - 1) << 24);

这里右边的掩码是多余的 - 在移位期间您将丢弃其他位; 对于内部段来说,它更重要。 - Marc Gravell
@Marc:你说得对,我已经考虑过了,在当前情况下它是多余的。通常来说,拥有防御性编程是好的。此外,u和West都是有符号整数。 - PepitoSh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接