看看这段c#代码:
byte x = 1;
byte y = 2;
byte z = x + y; // ERROR: Cannot implicitly convert type 'int' to 'byte'
在字节(或短)类型上执行的任何数学运算的结果都隐式地转换回整数。解决方案是显式地将结果转换回一个字节:
byte z = (byte)(x + y); // this works
我想知道的是为什么?是建筑吗?哲学吗?
我们有:
Int + Int = Int
长+长=长
浮+浮=浮
Double + Double = Double
所以为什么不呢:
字节+字节=字节
空头+空头=空头?
一点背景知识:我正在对“小数字”(即< 8)执行一个长列表的计算,并将中间结果存储在一个大数组中。使用字节数组(而不是int数组)更快(因为缓存命中)。但是大量的字节强制转换散布在代码中,使得代码更加难以阅读。
我想我以前在什么地方见过这个。从这篇文章,旧的新事物:
假设我们生活在一个幻想的世界
对'byte'的操作导致了什么
“字节”。
byte b = 32;
byte c = 240;
int i = b + c; // what is i?
在这个幻想世界里,i的值
应该是16岁!为什么?因为这两个
+操作符的操作数都是
字节,因此“b+c”的和计算为
一个字节,由于
整数溢出。(而且,正如我所指出的
之前,整数溢出是新的
安全攻击向量。)
编辑:Raymond实质上是在捍卫C和c++最初采用的方法。在评论中,他从语言向后兼容的角度为c#采用了相同的方法进行了辩护。
This was probably a practical decision on the part of the language designers. After all, an int is an Int32, a 32-bit signed integer. Whenever you do an integer operation on a type smaller than int, it's going to be converted to a 32 bit signed int by most any 32 bit CPU anyway. That, combined with the likelihood of overflowing small integers, probably sealed the deal. It saves you from the chore of continuously checking for over/under-flow, and when the final result of an expression on bytes would be in range, despite the fact that at some intermediate stage it would be out of range, you get a correct result.
另一种想法是:必须模拟这些类型上的溢出/欠流,因为它不会自然地发生在最可能的目标cpu上。何苦呢?
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)
Byte (int8)不是这些类型之一,在添加之前会自动强制转换为int32。(ECMA-335分区3节1.6)
除了所有其他伟大的评论,我想我要添加一个小花絮。很多评论都想知道为什么int、long和几乎任何其他数字类型都不遵循这个规则…返回一个“更大”的类型以响应算术。
A lot of answers have had to do with performance (well, 32bits is faster than 8bits). In reality, an 8bit number is still a 32bit number to a 32bit CPU....even if you add two bytes, the chunk of data the cpu operates on is going to be 32bits regardless...so adding ints is not going to be any "faster" than adding two bytes...its all the same to the cpu. NOW, adding two ints WILL be faster than adding two longs on a 32bit processor, because adding two longs requires more microops since you're working with numbers wider than the processors word.
I think the fundamental reason for causing byte arithmetic to result in ints is pretty clear and straight forward: 8bits just doesn't go very far! :D With 8 bits, you have an unsigned range of 0-255. That's not a whole lot of room to work with...the likelyhood that you are going to run into a bytes limitations is VERY high when using them in arithmetic. However, the chance that you're going to run out of bits when working with ints, or longs, or doubles, etc. is significantly lower...low enough that we very rarely encounter the need for more.
从字节到int的自动转换是合乎逻辑的,因为字节的规模是如此之小。从整型到长型,从浮点数到双精度浮点数等自动转换是不符合逻辑的,因为这些数字具有显著的比例。
这是我对这个话题的大部分回答,首先是针对这里的一个类似问题。
默认情况下,所有小于Int32的整数运算在计算前四舍五入到32位。结果为Int32的原因仅仅是让它在计算后保持原样。如果检查MSIL算术操作码,它们操作的唯一整型数字类型是Int32和Int64。这是“故意的”。
如果您希望结果以Int16格式返回,则在代码中执行强制转换或编译器(假设)在“底层”发出转换都无关紧要。
例如,要执行Int16算术:
short a = 2, b = 3;
short c = (short) (a + b);
这两个数字将扩展为32位,然后相加,然后截短为16位,这是MS所希望的。
使用短(或字节)的优势主要是在有大量数据(图形数据、流等)的情况下存储。
从.NET框架代码:
// bytes
private static object AddByte(byte Left, byte Right)
{
short num = (short) (Left + Right);
if (num > 0xff)
{
return num;
}
return (byte) num;
}
// shorts (int16)
private static object AddInt16(short Left, short Right)
{
int num = Left + Right;
if ((num <= 0x7fff) && (num >= -32768))
{
return (short) num;
}
return num;
}
使用。net 3.5及以上版本进行简化:
public static class Extensions
{
public static byte Add(this byte a, byte b)
{
return (byte)(a + b);
}
}
现在你可以做:
byte a = 1, b = 2, c;
c = a.Add(b);
我已经测试了字节和int之间的性能。
int值:
class Program
{
private int a,b,c,d,e,f;
public Program()
{
a = 1;
b = 2;
c = (a + b);
d = (a - b);
e = (b / a);
f = (c * b);
}
static void Main(string[] args)
{
int max = 10000000;
DateTime start = DateTime.Now;
Program[] tab = new Program[max];
for (int i = 0; i < max; i++)
{
tab[i] = new Program();
}
DateTime stop = DateTime.Now;
Debug.WriteLine(stop.Subtract(start).TotalSeconds);
}
}
使用字节值:
class Program
{
private byte a,b,c,d,e,f;
public Program()
{
a = 1;
b = 2;
c = (byte)(a + b);
d = (byte)(a - b);
e = (byte)(b / a);
f = (byte)(c * b);
}
static void Main(string[] args)
{
int max = 10000000;
DateTime start = DateTime.Now;
Program[] tab = new Program[max];
for (int i = 0; i < max; i++)
{
tab[i] = new Program();
}
DateTime stop = DateTime.Now;
Debug.WriteLine(stop.Subtract(start).TotalSeconds);
}
}
结果如下:
字节:3.57s 157mo, 3.71s 171mo, 3.74s 168mo, CPU ~= 30%
int: 4.05s 298mo, 3.92s 278mo, 4.28 294mo with CPU ~= 27%
结论:
字节使用更多的CPU,但它消耗更少的内存,它更快(可能是因为有更少的字节分配)