【问题标题】:Why Do Bytes Carryover?为什么字节结转?
【发布时间】:2016-02-02 23:55:20
【问题描述】:

我最近一直在玩一些字节数组(处理灰度图像)。一个字节的值可以是 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。这个结转的目的是什么?仅仅是因为我正在投射这个整数赋值吗?这在现实世界中什么时候有用?

【问题讨论】:

    标签: c#


    【解决方案1】:
    byte x = (byte) SetByte(259);
    Console.WriteLine(x);
    //output is 3
    

    SetByte 结果的转换将模 256 应用于您的整数输入,有效地丢弃超出字节范围的位。

    259 % 256 = 3

    为什么:实施者选择只考虑 8 个最低有效位,而忽略其余部分。

    【讨论】:

    • 这是有道理的,我理解这一点,但这并不能满足这样做的目的。为什么不设置为 255?还是我只需要接受它的本来面目而不问“为什么”? :D
    • “为什么”确实是更好的问题。实施者选择只考虑 8 个最低有效位,而忽略其余的。
    • 所以听起来可能会因语言而异。
    • 如果它设置为“255 或 0”,我希望我们会看到一个关于 SO 为什么是这种情况而不是结转的问题 :)
    【解决方案2】:

    编译 C# 时,您可以指定是否应在 checked or unchecked mode 中编译程序集(未选中为默认值)。您还可以通过使用 checkedunchecked 关键字来明确代码的某些部分。

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

    尝试以下方法:

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

    你会看到它抛出一个OverflowException

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

    另一个原因是钳位会导致数据丢失,这可能是不可取的。我不容忍以下代码,但我看过(见this answer):

    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 对 What is the best algorithm for an overridden System.Object.GetHashCode 的回答。如果溢出意味着我们被限制为Int32.MaxValue,那么体面地实现GetHashCode 将是一场噩梦。

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

      • 这几乎就是我为处理超出字节范围的值所做的工作。
      • 如果你想知道为什么在强制转换时这种行为不是默认行为,这是因为简单地切断字节非常快(一条或几条指令),而钳制需要 if-else,这需要缓慢的条件跳转.
      【解决方案4】:

      做模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 over = 260 无法编译。您必须通过转换能够保持值 260 的类型来进行分配。
      【解决方案5】:

      对于直接类型转换的情况(使用(byte) 时),边界控件不活动以避免性能下降。

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

      或者您可以创建一个函数来执行此检查,如下面的另一个人所述。 如果性能是关键,请尝试添加属性[MethodImpl(MethodImplOptions.AggressiveInlining)] 到那个函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-11
        • 2010-11-08
        • 2021-02-04
        • 2015-07-06
        • 1970-01-01
        相关资源
        最近更新 更多