为什么ushort + ushort 等于int? [英] Why is ushort + ushort equal to int?

查看:71
本文介绍了为什么ushort + ushort 等于int?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

之前今天我试图添加两个 ushorts,但我注意到我必须将结果转换回 ushort.我认为它可能已经变成了一个 uint(以防止可能的意外溢出?),但令我惊讶的是它是一个 int (System.Int32).

Previously today I was trying to add two ushorts and I noticed that I had to cast the result back to ushort. I thought it might've become a uint (to prevent a possible unintended overflow?), but to my surprise it was an int (System.Int32).

这是否有一些巧妙的原因,或者可能是因为 int 被视为基本"整数类型?

Is there some clever reason for this or is it maybe because int is seen as the 'basic' integer type?

示例:

ushort a = 1;
ushort b = 2;

ushort c = a + b; // <- "Cannot implicitly convert type 'int' to 'ushort'. An explicit conversion exists (are you missing a cast?)"
uint d = a + b; // <- "Cannot implicitly convert type 'int' to 'uint'. An explicit conversion exists (are you missing a cast?)"

int e = a + b; // <- Works!

就像 GregS 的回答一样,C# 规范说两个操作数(在本例中为a"和b")都应该转换为 int.我对为什么这是规范的一部分的根本原因感兴趣:为什么 C# 规范不允许直接对 ushort 值进行操作?

Like GregS' answer says, the C# spec says that both operands (in this example 'a' and 'b') should be converted to int. I'm interested in the underlying reason for why this is part of the spec: why doesn't the C# spec allow for operations directly on ushort values?

推荐答案

简单而正确的答案是因为 C# 语言规范是这样说的".

The simple and correct answer is "because the C# Language Specification says so".

显然你对这个答案不满意,想知道为什么这么说".您正在寻找可靠和/或官方来源",这会有点困难.这些设计决策是在很久以前做出的,13 年是软件工程领域的许多狗年.它们是由埃里克·利珀特 (Eric Lippert) 所称的老前辈"制作的,他们已经转向更大更好的事物,并且不会在此处发布答案以提供官方来源.

Clearly you are not happy with that answer and want to know "why does it say so". You are looking for "credible and/or official sources", that's going to be a bit difficult. These design decisions were made a long time ago, 13 years is a lot of dog lives in software engineering. They were made by the "old timers" as Eric Lippert calls them, they've moved on to bigger and better things and don't post answers here to provide an official source.

然而,它可以推断出来,但有可能仅仅是可信的.任何托管编译器,如 C# 的,都有为 .NET 虚拟机生成代码所需的约束.在 CLI 规范中仔细(并且相当易读)描述了这些规则.它是 Ecma-335 规范,您可以从这里免费下载从这里.

It can be inferred however, at a risk of merely being credible. Any managed compiler, like C#'s, has the constraint that it needs to generate code for the .NET virtual machine. The rules for which are carefully (and quite readably) described in the CLI spec. It is the Ecma-335 spec, you can download it for free from here.

转到第三部分,第 3.1 和 3.2 章.它们描述了可用于执行加法的两个 IL 指令,addadd.ovf.单击表 2二进制数字运算"的链接,它描述了这些 IL 指令允许的操作数.请注意,此处仅列出了几种类型.byte 和 short 以及所有无符号类型都丢失了.只允许 int、long、IntPtr 和浮点数(float 和 double).例如,对于由 x 标记的附加约束,您不能将 int 添加到 long 中.这些限制并非完全是人为的,而是基于您可以在可用硬件上合理高效地完成的事情.

Turn to Partition III, chapter 3.1 and 3.2. They describe the two IL instructions available to perform an addition, add and add.ovf. Click the link to Table 2, "Binary Numeric Operations", it describes what operands are permissible for those IL instructions. Note that there are just a few types listed there. byte and short as well as all unsigned types are missing. Only int, long, IntPtr and floating point (float and double) is allowed. With additional constraints marked by an x, you can't add an int to a long for example. These constraints are not entirely artificial, they are based on things you can do reasonably efficient on available hardware.

任何托管编译器都必须处理这个问题才能生成有效的 IL.这并不难,只需将 ushort 转换为表中更大的值类型,这种转换始终有效.C# 编译器选择 int,即出现在表中的下一个较大的类型.或者一般来说,将任何操作数转换为下一个最大的值类型,使它们具有相同的类型并满足表中的约束.

Any managed compiler has to deal with this in order to generate valid IL. That isn't difficult, simply convert the ushort to a larger value type that's in the table, a conversion that's always valid. The C# compiler picks int, the next larger type that appears in the table. Or in general, convert any of the operands to the next largest value type so they both have the same type and meet the constraints in the table.

然而,现在出现了一个新问题,这个问题使 C# 程序员非常疯狂.添加的结果是提升的类型.在你的情况下,这将是 int.因此,将两个 ushort 值相加,例如 0x9000 和 0x9000,会得到一个完全有效的 int 结果:0x12000.问题是:这是一个不适合 ushort 的值.值溢出.但是它在 IL 计算中没有溢出,只有在编译器试图将它塞回 ushort 时才会溢出.0x12000 被截断为 0x2000.一个令人眼花缭乱的不同值,只有当您用 2 或 16 个手指而不是 10 个手指数时才有意义.

Now there's a new problem however, a problem that drives C# programmers pretty nutty. The result of the addition is of the promoted type. In your case that will be int. So adding two ushort values of, say, 0x9000 and 0x9000 has a perfectly valid int result: 0x12000. Problem is: that's a value that doesn't fit back into an ushort. The value overflowed. But it didn't overflow in the IL calculation, it only overflows when the compiler tries to cram it back into an ushort. 0x12000 is truncated to 0x2000. A bewildering different value that only makes some sense when you count with 2 or 16 fingers, not with 10.

值得注意的是 add.ovf 指令没有处理这个问题.它是用于自动生成溢出异常的指令.但事实并非如此,转换后的整数的实际计算并没有溢出.

Notable is that the add.ovf instruction doesn't deal with this problem. It is the instruction to use to automatically generate an overflow exception. But it doesn't, the actual calculation on the converted ints didn't overflow.

这是真正的设计决策发挥作用的地方.前辈们显然认为简单地将 int 结果截断为 ushort 是一个错误工厂.必然是.他们决定您必须承认您知道添加可能会溢出,并且如果发生这种情况也没关系.他们把它变成了你的问题,主要是因为他们不知道如何让它成为他们的问题并且仍然生成高效的代码.你必须投.是的,这太令人抓狂了,我相信你也不希望出现这个问题.

This is where the real design decision comes into play. The old-timers apparently decided that simply truncating the int result to ushort was a bug factory. It certainly is. They decided that you have to acknowledge that you know that the addition can overflow and that it is okay if it happens. They made it your problem, mostly because they didn't know how to make it theirs and still generate efficient code. You have to cast. Yes, that's maddening, I'm sure you didn't want that problem either.

非常值得注意的是,VB.NET 设计者对这个问题采用了不同的解决方案.他们实际上把它变成了他们的问题并且没有推卸责任.您可以添加两个 UShort 并将其分配给一个 UShort 而无需演员表.不同之处在于VB.NET 编译器实际上会生成额外 IL 来检查溢出情况.这不是廉价的代码,使每个简短的添加速度慢 3 倍.但除此之外,这也解释了为什么 Microsoft 维护两种语言的原因,这些语言在其他方面具有非常相似的功能.

Quite notable is that the VB.NET designers took a different solution to the problem. They actually made it their problem and didn't pass the buck. You can add two UShorts and assign it to an UShort without a cast. The difference is that the VB.NET compiler actually generates extra IL to check for the overflow condition. That's not cheap code, makes every short addition about 3 times as slow. But otherwise the reason that explains why Microsoft maintains two languages that have otherwise very similar capabilities.

长话短说:您付出了代价,因为您使用的类型与现代 CPU 架构不太匹配.这本身就是使用 uint 而不是 ushort 的一个很好的理由.从 ushort 中获得牵引力是很困难的,在操作它们的成本超过节省的内存之前,您将需要很多它们.不仅仅是因为有限的 CLI 规范,x86 内核需要额外的 cpu 周期来加载 16 位值,因为机器代码中的操作数前缀字节.实际上不确定今天是否仍然如此,当我仍然注意计数周期时,它曾经回来过.一年前的一只狗.

Long story short: you are paying a price because you use a type that's not a very good match with modern cpu architectures. Which in itself is a Really Good Reason to use uint instead of ushort. Getting traction out of ushort is difficult, you'll need a lot of them before the cost of manipulating them out-weighs the memory savings. Not just because of the limited CLI spec, an x86 core takes an extra cpu cycle to load a 16-bit value because of the operand prefix byte in the machine code. Not actually sure if that is still the case today, it used to be back when I still paid attention to counting cycles. A dog year ago.

请注意,通过让 C# 编译器生成与 VB.NET 编译器生成的代码相同的代码,您可以对这些丑陋和危险的强制转换感觉更好.所以当演员被证明是不明智的时,你会得到一个 OverflowException.使用项目> 属性> 构建选项卡> 高级按钮> 勾选检查算术溢出/下溢"复选框.仅用于调试版本.顺便说一下,为什么这个复选框没有被项目模板自动打开是另一个非常神秘的问题,这个决定是很久以前做出的.

Do note that you can feel better about these ugly and dangerous casts by letting the C# compiler generate the same code that the VB.NET compiler generates. So you get an OverflowException when the cast turned out to be unwise. Use Project > Properties > Build tab > Advanced button > tick the "Check for arithmetic overflow/underflow" checkbox. Just for the Debug build. Why this checkbox isn't turned on automatically by the project template is another very mystifying question btw, a decision that was made too long ago.

这篇关于为什么ushort + ushort 等于int?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆