我最近一直在玩一些字节数组(处理灰度图像)。一个字节的值可以是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。这个结转的目的是什么?是不是因为我投了这个整数赋值?什么时候在现实世界中有用?
答案 0 :(得分:8)
byte x = (byte) SetByte(259);
Console.WriteLine(x);
//output is 3
SetByte结果的强制转换将模256应用于整数输入,有效地丢弃超出字节范围的位。
259%256 = 3
为什么:实施者选择只考虑8个最低有效位,忽略其余位。
答案 1 :(得分:6)
编译C#时,您可以指定是否应在checked or unchecked mode中编译程序集(默认情况下未选中)。您还可以使用checked
或unchecked
关键字明确表达某些代码部分。
您当前正在使用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的回答。如果溢出意味着我们被限制在GetHashCode
,那么实施Int32.MaxValue
将是一场噩梦。
答案 2 :(得分:2)
方法SetByte
无关紧要,简单地转换(byte) 259
也会产生3
,因为向下转换整数类型实现为切割字节。
您可以创建自定义钳位功能:
public static byte Clamp(int n) {
if(n <= 0) return 0;
if(n >= 256) return 255;
return (byte) n;
}
答案 3 :(得分:2)
算术模2 ^ n可以使不同方向的溢出错误相互抵消。
byte under = -12; // = 244
byte over = (byte) 260; // = 4
byte total = under + over;
Console.WriteLine(total); // prints 248, as intended
如果.NET有溢出饱和,那么上面的程序将打印错误的答案255。
答案 4 :(得分:0)
对于具有直接类型转换(使用 Map<Job, List<Job>> jobsMap = jobs.stream().collect(Collectors.groupingBy(job -> job));
jobsMap.keySet().stream()
.peek(job -> jobsMap.get(job).stream().forEach(j -> job.getNetworks().addAll(j.getNetworks())))
.sorted(Comparator.comparing((Job o) -> Objects.firstNonNull(o.getEndYear(), Integer.MAX_VALUE))
.reversed())
.collect(Collectors.toList());
时)以避免性能降低的情况,边界控件不活动。
FYI,大多数以byte操作数操作的结果是整数,不包括位操作。使用(byte)
,您将获得溢出异常,您可以通过为目标分配255来处理它。
或者您可以创建一个功能来执行此检查,如下面的另一个人所提到的。
如果perfomanse是键,请尝试添加属性Convert.ToByte()
那个功能。