为什么字节会结转

本文关键字:字节 为什么 | 更新日期: 2023-09-27 18:32:54

我最近一直在玩一些字节数组(处理灰度图像(。字节的值可以是 0-255。我正在修改字节,遇到了我分配给字节的值超出字节范围的情况。它对我正在玩的图像做了意想不到的事情。

我写了一个测试,并了解到字节会延续。例:

private static int SetByte(int y)
{
    return y;
}
.....
byte x = (byte) SetByte(-4);
Console.WriteLine(x);
//output is 252

有结转!当我们反其道而行之时,也会发生这种情况。

byte x = (byte) SetByte(259);
Console.WriteLine(x);
//output is 3

我本来希望它在第一种情况下将其设置为 255,在第二种情况下设置为 0。 这种结转的目的是什么?仅仅是因为我正在转换这个整数赋值吗?这在现实世界中什么时候有用?

为什么字节会结转

byte x = (byte) SetByte(259);
Console.WriteLine(x);
//output is 3

SetByte 结果的强制转换将模 256 应用于整数输入,从而有效地删除字节范围之外的位。

259 % 256 = 3

原因:实施者选择只考虑 8 个最低有效位,忽略其余位。

编译 C# 时,可以指定是在选中还是未选中模式下编译程序集(默认为未选中(。您还可以通过使用 checkedunchecked 关键字使代码的某些部分显式化。

您当前正在使用unchecked忽略算术溢出并截断值的模式。checked模式将检查可能的溢出,如果遇到溢出,则会抛出。

请尝试以下操作:

int y = 259;
byte x = checked((byte)y);

你会看到它抛出了一个OverflowException.

unchecked模式下的行为是截断而不是钳位的原因主要是出于性能原因,每个未经检查的强制转换都需要条件逻辑来钳制值,而大多数时候它是不必要的,并且可以手动完成。

另一个原因是,夹紧会导致数据丢失,这可能是不希望的。我不容忍以下代码,但已经看到了它(请参阅此答案(:

int input = 259;
var firstByte = (byte)input;
var secondByte = (byte)(input >> 8);
int reconstructed = (int)firstByte + (secondByte << 8);
Assert.AreEqual(reconstructed, input);

如果firstByte不是 3 个,这根本行不通。

我最常依赖数字结转的地方之一是在实现GetHashCode()时,请参阅Jon Skeet的覆盖System.Object.GetHashCode的最佳算法是什么答案。如果溢出意味着我们不得不Int32.MaxValue,那么体面地实施GetHashCode将是一场噩梦。

方法SetByte是无关紧要的,简单地转换(byte) 259也会导致3,因为向下转换整数类型是作为字节的切割实现的。

您可以创建自定义钳位函数:

 public static byte Clamp(int n) {
     if(n <= 0) return 0;
     if(n >= 256) return 255;
     return (byte) n;
 }

做算术模 2^n 使得不同方向的溢出错误可以相互抵消。

byte under = -12; // = 244
byte over  = (byte) 260; // = 4
byte total = under + over;
Console.WriteLine(total); // prints 248, as intended

如果 .NET 反而溢出饱和,则上述程序将打印错误答案 255。

对于具有直接类型强制转换(使用 (byte) 时(的情况,边界控件不处于活动状态,以避免性能降低。

仅供参考,大多数具有字节操作数的操作的结果是整数,不包括位操作。使用 Convert.ToByte(),您将获得溢出异常,您可以通过将 255 分配给目标来处理它。

或者你可以创建一个功能来做这个检查,正如下面的另一个人提到的。如果 perfomanse 是键,请尝试添加属性[MethodImpl(MethodImplOptions.AggressiveInlining)]到那个功能。