携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
前言
本文总结了C#中的值类型,特别是数值类型的细节问题。虽然数值类型的使用是一个基础知识,但语言特性中有一些不常见的点也是值得了解的。
C#预定义值类型包含
- 数值类型
- 有符号整数类型: sbyte, short, int, long
- 无符号整数类型: byte, ushort, uint, ulong
- 实数类型:float, dobule, decimal
- 逻辑类型
- bool
- 字符类型
- char
预定义类型是C# Framework中特定类型的别名
- 例如
int其实是System.Int32类型 - 它们只是使用的语法不太一样,其实
int可以看做是编译器的语法糖
除decimal之外的值类型都是原子类型(Primitive type)
- 原子类型在编译之后的代码中有直接的指令支持,并且通常有处理器的直接支持。
- 例如
float类型,编译后为IEEE浮点数编码。 - System.IntPtr和System.UIntPtr也是原子类型。
数值类型范围
- sbyte/byte: 8位
- short/ushort: 16位
- int/uint: 32位
- long/ulong: 64位
- float: 32位
- double: 64位
- decimal: 128位
C#7引入的数字下划线分隔
- 可以在数字字面量任意位置插入下划线,这是为了增强数字的可读性
- 例如: int some_larg_number = 1_234_567;
数字字面量的类型推断
- 在没有显示指定数字字面量的后缀时,编译器会对其类型进行推断
- 编译器默认推断数字字面量的类型为double或整数类型。
- 如果字面量包含小数点,或者指数符号E,则推断为double
- 否则,字面量类型按照这个序列去尝试推断:
int, uint, long, ulong,当某类型可以容纳字面量的值时,则推断为该类型。 - 例如:1.0的类型为double,1的类型为int,0XF0000000的类型为uint
数字后缀
-
后缀添加在数字字面量后面,明确的指定字面量的类型,大小写均可
-
后缀包含F, D, M, U, L, UL:
- float f = 1.0f;
- double d = 1D;
- decimal d = 1.0M;
- uint i = 1U;
- long i = 1L;
- ulong i = 1UL;
-
后缀 U 和 L 几乎用不上,因为 uint, long, ulong总是能被推断出来,或者从int隐式转换
-
后缀 D 也是多余的,因为只要有小数点,就能推断为double,而小数点总是可以使用的,例如 2.0
-
后缀 F 和 M 是最有用的。比如对于float类型,必须使用后缀F才能指定。例如: float f = 3.14f; 如果不使用后缀F,则3.14肯定就推断为double了,且不能从double隐式转换到float,因此会造成一个编译错误。
溢出和溢出检测
整数类型计算造成溢出时默认情况不会抛出异常,使用checked操作符可以检查溢出,当溢出发生时抛出OverflowException异常。例如:
int a = 1234567;
int b = 1234567;
int c = checked(a * b); //检查单个表达式
//检查一个块中的所有表达式
checked
{
c = a * b;
c = a * a;
}
- 注意:浮点类型是不支持
checked操作符的 - 通过编译器选项可以默认打开checked,即不需要显示使用
checked操作符即可以进行检查。这种情况下,如果想要对于个别表达式不进行检查,可以使用unchecked操作符。 - 常量表达式总会进行checked检查,如果不想检查,可以使用unchecked。
特殊浮点数值
特殊浮点数值常量
- NaN: double.NaN 和 float.NaN
- 正无穷大 double.PositiveInfinity和float.PositiveInfinity
- 负无穷大 double.NegativeInfinity和float.NegativeInfinity
- 负0:-0.0 和 -0.0f
除0的结果
非0数值除以0得到无穷大
- 1.0/0.0 和 -1.0/-0.0 得到正无穷大
- -1.0/0.0 和 1.0/-0.0 得到负无穷大
0除0
- 0.0/0.0 得到 NaN
无穷大相减
- (1.0/0.0) - (1.0./0.0) 得到NaN
NaN比较
- 使用 == 和 NaN 比较,结果总是False
- 甚至 == 的两端都是NaN,结果也是False,例如: 0.0/0.0 == double.NaN 结果为False
- 使用object.Equals比较时,两个NaN是相等的,例如: object.Equals(0.0/0.0, double.NaN);结果为True
测试NaN
使用 double.IsNaN() 和 float.IsNaN()
NaN的用处
可以使用NaN表示一个特定值,表示未设置等情况,使用IsNaN测试。
浮点数误差
- float和double内部表达都是基于2进制,因此它们只能精确表示基于2进制的数值。而很多基于10进制的小数则不能精确表示。而decimal类型是基于10进制的,因此可以精确表示10进制数。
- 由于误差的存在,浮点数之间的比较要特别小心。一般会使用一个容错值进行比较。