byte + byte = int ...为什么?

Rob*_*ino 356 c# type-conversion

看看这个C#代码:

byte x = 1;
byte y = 2;
byte z = x + y; // ERROR: Cannot implicitly convert type 'int' to 'byte'
Run Code Online (Sandbox Code Playgroud)

byte(或short)类型执行的任何数学运算的结果都隐式地转换回整数.解决方案是将结果显式地转换回一个字节:

byte z = (byte)(x + y); // this works
Run Code Online (Sandbox Code Playgroud)

我想知道的是为什么?它是建筑吗?哲学?

我们有:

  • int+ int=int
  • long+ long=long
  • float+ float=float
  • double+ double=double

那么为什么不呢:

  • byte+ byte=byte
  • short+ short= short

一点背景:我正在对"小数字"(即<8)执行一长串计算,并将中间结果存储在一个大数组中.使用字节数组(而不是int数组)更快(因为缓存命中).但是通过代码传播的大量字节转换使得它更加难以理解.

azh*_*lov 220

代码段的第三行:

byte z = x + y;
Run Code Online (Sandbox Code Playgroud)

实际意味着

byte z = (int) x + (int) y;
Run Code Online (Sandbox Code Playgroud)

因此,对字节没有+操作,首先将字节转换为整数,并且添加两个整数的结果是(32位)整数.

  • 这必须是最正确,最简洁的答案.没有操作数可以在字节之间添加,所以不是解释为什么"添加两个字节"工作与否(*它从未发生*),这清楚地说明了为什么结果是一个int,因为唯一发生的事情是**增加2个投注**. (33认同)
  • 那是因为字节没有+操作(见上文).尝试字节z =(字节)((int)x +(int)y) (10认同)
  • 我头晕目眩地读了所有其他答案(对Jon Skeet先生没有冒犯).我发现这是最简单的答案,正确描述了幕后发生的事情.谢谢! (2认同)

Jon*_*eet 170

就"为什么会发生这种情况"而言,这是因为没有任何运算符由C#定义用于byte,sbyte,short或ushort的算术,就像其他人所说的那样.这个答案是为什么没有定义那些运营商.

我相信这基本上是为了表现.处理器具有本机操作,可以非常快速地使用32位进行算术运算.可以自动完成从结果到字节的转换,但在您实际上不需要该行为的情况下会导致性能损失.

认为这是在一个带注释的C#标准中提到的.看着...

编辑:令人讨厌的是,我现在已经查看了带注释的ECMA C#2规范,带注释的MS C#3规范和注释CLI规范,据我所知,他们都没有提到这一点.我确定我已经看到了上面提到的原因,但如果我知道在哪里,我会被打击.道歉,参考粉丝:(

  • (只是为了澄清,我并不是真的想要你.似乎每个人都有他们自己的低估标准,这没关系.如果我认为它是积极的有害而不仅仅是非理想,我只会回答一个答案. ) (55认同)
  • 您是否已将您认为不是最好的答案的*每个答案都投了?;) (41认同)
  • IMO获得顶级"最佳"答案的最佳方式是赞成这一点.老实说,我认为这里提供的信息最多的是Eric在问题中的评论...但除此之外,对于设计视角(与"编译器正在做什么"的观点相反),我认为没有*是*超越"表现"的答案.特别是,我真的不买"它防止溢出"的论点(17票),因为这会建议int + int = long. (23认同)
  • 我使用投票作为工具来获得顶部的"最佳"答案.实际上我发现你在答案中没有说太多,这是我投票的主要原因.另一个原因可能是我的主观感觉,即你的代表在投票方面给你一个很大的奖励,而你在"更好"的答案之上登陆. (20认同)
  • 我很遗憾地说,但我觉得这不是最好的答案. (14认同)
  • 我认为对于对称性,32 + 240变为16与int.MaxValue + 1一样逻辑为int.MinValue(模数Eric的关于字节的注释实际上不是一个数字,而是一个比特集合).很高兴我们在C#中有一个检查上下文的概念...... (4认同)
  • @Jon他不仅做到了(可能)他在这里发表了评论. (3认同)
  • 如果你指的是Michael Petrotta的答案:主要论点是IMO Raymond Chen的博客文章和他出色的"假设"例子.IMO没有人会建议32 + 240必须是16只因为这些数字存储为字节.回到你的第二个评论:你是对的.既然你的回答完全没有坏或错,我没有理由对它进行投票. (2认同)
  • 有些人似乎不喜欢这种"无聊"的答案,因为它太实际了.他们想要更概念化的东西.对我来说,这个实际的答案似乎更加合理:当你设计一个规范时,你还需要考虑实际因素.int被设计为使用CPU添加,一个字节用于存储数据.添加时,使用为添加而优化的数据类型. (2认同)
  • 我不明白为什么有些人会拒绝这个答案; 首先要指出核心是*"为什么那些运营商没有被定义"*,比简单说*"他们不是"*更有帮助. (2认同)
  • @KevinMuhuri:是的,因为 `+=` 是一个复合赋值运算符,其中有隐式强制转换。 (2认同)

Mic*_*tta 67

以为我以前见过这个.从这篇文章,旧的新事物:

假设我们生活在一个幻想世界中,'byte'上的操作导致'byte'.

byte b = 32;
byte c = 240;
int i = b + c; // what is i?
Run Code Online (Sandbox Code Playgroud)

在这个幻想世界中,我的价值将是16!为什么?因为+运算符的两个操作数都是字节,所以总和"b + c"被计算为一个字节,由于整数溢出导致16.(而且,正如我前面提到的,整数溢出是新的安全攻击向量.)

编辑:雷蒙德基本上是在捍卫C和C++最初采用的方法.在评论中,他以语言向后兼容性为由捍卫了C#采用相同方法的事实.

  • 使用整数,如果我们添加它们并且它溢出它不会自动将其转换为不同的数据类型,但为什么要使用byte? (42认同)
  • 究竟.如果这是一个安全措施,它是一个非常糟糕的实施;) (27认同)
  • @ Longhorn213:是的,这就是Ryan所说的:int数学可以溢出,但是int数学不会返回多头. (8认同)
  • @Ryan:对于C#语言设计师而言,"懒惰"是一项非常重要的指控,因为它基本上像原始数学一样.如果你想指责它们,那就"使它与C/C++过度向后兼容". (5认同)
  • 有了它就会溢出.尝试添加int.MaxValue + 1得到-2147483648而不是2147483648. (2认同)
  • "与C/C++过度向后兼容"可能是更好的方式.但是,我们可以将问题扩展到为什么C以这种方式设计并且我们重新开始. (2认同)

Alu*_*ord 57

C#

ECMA-334声明添加仅在int + int,uint + uint,long + long和ulong + ulong(ECMA-334 14.7.4)上定义为合法.因此,这些是关于14.4.2要考虑的候选操作.因为存在从byte到int,uint,long和ulong的隐式转换,所有加法函数成员都是适用于14.4.2.1的函数成员.我们必须找到14.4.2.3中规则的最佳隐式转换:

铸造(C1)到int(T1)比铸造(C2)到uint(T2)或ulong(T2)要好,因为:

  • 如果T1为int且T2为uint或ulong,则C1为更好的转换.

将(C1)转换为int(T1)比将(C2)转换为long(T2)更好,因为存在从int到long的隐式转换:

  • 如果存在从T1到T2的隐式转换,并且不存在从T2到T1的隐式转换,则C1是更好的转换.

因此使用int + int函数,它返回一个int.

这是一个非常漫长的方式,它说它深深埋藏在C#规范中.

CLI

CLI仅对6种类型(int32,native int,int64,F,O和&)进行操作.(ECMA-335分区3部分1.5)

字节(int8)不是这些类型之一,并且在添加之前自动强制转换为int32.(ECMA-335分区3部分1.6)


Chr*_*her 26

表示添加字节并将结果截断回字节的低效率的答案是不正确的.x86处理器具有专门针对8位数量的整数运算而设计的指令.

实际上,对于x86/64处理器,由于必须解码的操作数前缀字节,执行32位或16位操作的效率低于64位或8位操作.在32位机器上,执行16位操作需要相同的惩罚,但仍有专用的操作码用于8位操作.

许多RISC架构具有类似的本机字/字节有效指令.那些通常没有存储和转换为某些位长度的符号值的那些.

换句话说,这个决定必须基于对字节类型的感知,而不是由于硬件的潜在低效率.


BFr*_*ree 13

我记得曾经从Jon Skeet读过一些东西(现在找不到,我会继续看),关于字节实际上不会超载+运算符.实际上,当在示例中添加两个字节时,每个字节实际上都是隐式转换为int.结果显然是一个int.现在为什么这是这样设计的,我会等Jon Skeet自己发帖:)

编辑:找到它!关于这个题目伟大的信息在这里.


sam*_*moz 7

这是因为溢出和携带.

如果添加两个8位数字,它们可能会溢出到第9位.

例:

  1111 1111
+ 0000 0001
-----------
1 0000 0000
Run Code Online (Sandbox Code Playgroud)

我不知道肯定,但我认为ints,longsdoubles被赋予了更多的空间,因为他们是相当大的,因为它是.此外,它们是4的倍数,由于内部数据总线的宽度为4字节或32位(64位现在变得更普遍),因此它们对计算机来说更有效.字节和短字的效率要低一些,但它们可以节省空间.

  • 但是较大的数据类型不遵循相同的行为. (22认同)
  • 溢出的问题暂时搁置.如果您采用逻辑并将其应用于语言,那么所有数据类型在添加算术后将返回更大的数据类型,绝对不是这种情况.int + int = int,long + long = long.我认为问题在于不一致. (12认同)
  • 哦,关于"可能溢出"的文章,为什么字节+字节=短? (10认同)

Rya*_*yan 5

从C#语言规范1.6.7.5 7.2.6.2二进制数字促销它将两个操作数转换为int,如果它不能适合其他几个类别.我的猜测是他们没有重载+运算符以将byte作为参数,但希望它在某种程度上正常运行,因此他们只使用int数据类型.

C#语言规范