page contents

c# - 为什么ushort + ushort等于int?

本文讲述了c# - 为什么ushort + ushort等于int?具有很好的参考价值,希望对大家有所帮助。一起跟随六星小编过来看看吧,具体如下:

attachments-2022-11-ZgBa23z86365c73a13cf3.png本文讲述了c# - 为什么ushort + ushort等于int?具有很好的参考价值,希望对大家有所帮助。一起跟随六星小编过来看看吧,具体如下:

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

是否有一些聪明的原因,或者可能是因为int被视为“基本”整数类型?

例:

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值进行操作?

最佳答案

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

显然,您对该答案不满意,并且想知道“为什么这么说”。您正在寻找“可信和/或官方来源”,这将有些困难。这些设计决策是在很久以前做出的,在软件工程 Realm 已经有13年的历史了。它们由埃里克·利珀特(Eric Lippert)称呼他们的“老 friend ”制作,他们已经着手做更大更好的事情,因此不在此处发布答案以提供官方消息。

但是,可以推断出它只是可信的风险。任何托管编译器(如C#编译器)都具有这样的约束,即它需要为.NET虚拟机生成代码。 CLI规范中仔细(且易于理解)描述了这些规则。它是Ecma-335的规格,您可以免费下载from here

转到分区III,第3.1和3.2章。它们描述了可用于执行加法的两条IL指令addadd.ovf。单击表2“二进制数值运算”的链接,它描述了那些IL指令允许使用的操作数。请注意,这里仅列出了几种类型。缺少字节和短以及所有无符号类型。仅允许使用int,long,IntPtr和浮点数(float和double)。例如,在用x标记其他约束的情况下,不能将int添加到long中。这些约束并非完全是人为的,它们是基于您可以在可用硬件上合理有效地完成的事情。

任何托管编译器都必须处理此问题才能生成有效的IL。这并不困难,只需将ushort转换为表中较大的值类型,该转换始终有效。 C#编译器选择int,这是表中显示的下一个更大的类型。或通常,将任何操作数转换为下一个最大值类型,以使它们具有相同的类型并满足表中的约束。

但是,现在有了一个新问题,这个问题使C#程序员更加疯狂。添加的结果属于提升类型。在您的情况下,它将为int。因此,将两个ushort值(例如0x9000和0x9000)相加会得到一个完全有效的int结果:0x12000。问题是:这是一个不适合ushort的值。该值溢出。但是它在IL计算中没有溢出,仅在编译器尝试将其填充到ushort中时才溢出。 0x12000被截断为0x2000。令人困惑的不同值,只有在用2或16根手指而不是10根手指计数时才有意义。

值得注意的是,add.ovf指令不处理此问题。这是用于自动生成溢出异常的指令。但事实并非如此,对转换后的int的实际计算并未溢出。

这是真正的设计决策发挥作用的地方。以前的人显然认为,将int结果截断为ushort就是一个错误工厂。必然是。他们决定,您必须承认自己知道添加可能会溢出,并且可以进行添加。他们把它变成了您的问题,主要是因为他们不知道如何使其成为自己的问题,而是仍然生成高效的代码。你必须投。是的,这令人发疯,我敢肯定您也不希望这个问题。

值得注意的是,VB.NET设计人员针对此问题采用了不同的解决方案。他们实际上解决了他们的问题,但并没有因此而失败。您可以添加两个UShorts并将其分配给不带强制转换的UShort。区别在于VB.NET编译器实际上会生成额外的IL以检查溢出条件。那不是便宜的代码,使每次添加的速度慢大约3倍。但是除此之外,这也解释了为什么Microsoft维护两种具有非常相似功能的语言的原因。

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

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

更多相关技术内容咨询欢迎前往并持续关注六星社区了解详情。

长按或扫描下方二维码,免费获取 Python公开课和大佬打包整理的几百G的学习资料,内容包含但不限于Python电子书、教程、项目接单、源码等等

attachments-2022-10-kwwbZ9WG6347756cbf77c.jpg

  • 发表于 2022-11-05 10:15
  • 阅读 ( 572 )
  • 分类:C/C++开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
轩辕小不懂
轩辕小不懂

2403 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1474 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章