数据类型转换

1 常用的数据转换方法

数据转换是经常要用到的,C# 中数据转换的方法很多,拿将目标对象转换为整型(int)来讲,有四种方法:分别为 (int)int.Parse()int.TryParse()Convert.ToInt32()
下面从被转换对象说起,在我们实际开发项目的过程中,我们碰到需要被转换的类型大概有 3 大类,分别是空值(null)、数字类型(包含floatdoubleintlong等)和字符串(string)这3类。

1.1 测试

  • NULL

    int a = Convert.ToInt32(null);  //a = 0
    int b;
    bool rlt = int.TryParse(null, out b);  // b = 0,rlt = false
    int c = int.Parse(null);  // 错误,Value cannot be null
    int d = (int)null;  // 错误,Cannot convert null to 'int' because it is a non-nullable value type
    
  • 数字类型 doublelong

    double m = 1.232d;
    int a = Convert.ToInt32(m);  // a = 1
    int b;
    bool rlt = int.TryParse(m.ToString(), out b);  // b = 0,rlt = false
    int c = int.Parse(m.ToString());  // 错误,Input string was not in a correct format
    int d = (int)m;  // d = 1
    
    long m = 9223372036854775807;
    int a = Convert.ToInt32(m);  // 错误,Value was either too large or too small for an Int32
    int b;
    bool rlt = int.TryParse(m.ToString(), out b);  // b = 0,rlt = false
    int c = int.Parse(m.ToString());  // 错误,Value was either too large or too small for an Int32
    int d = (int)m;  // d = -1
    
  • 字符串类型

    string m = "1.32";
    int a = Convert.ToInt32(m);  // 错误,Input string was not in a correct format
    int b;
    bool rlt = int.TryParse(m, out b);  // b = 0,rlt = false
    int c = int.Parse(m);  // 错误,Input string was not in a correct format
    int d = (int)m;  // 错误,Cannot convert type 'string' to 'int'
    

1.2 结论

  1. 对于转换对象
    • Convert.ToInt32() 可以为多种类型(例出数字类型外 boolDateTime)。
    • int.TryParse()int.Parse()只能是整型字符串类型(即各种整型 ToString() 之后的形式,不能为浮点型,否则 int.Parse() 就会出现输入的字符串格式不正确的错误,int.TryParse() 也会返回 false,输出参数为 0)。
    • (int) 只能是数字类型(例 floatintuint等)。
  2. 对于空值 NULL
    • 从运行报错的角度讲,(int) 强制转换和 int.Parse() 都不能接受 null
    • Convert.ToInt32() 其实是在转换前先做了一个判断,参数如果为 null,则直接返回 0,否则就调用 int.Parse() 进行转换。
    • int.TryParse() 其实是对 int.Parse() 做了一个异常处理,如果出现异常则返回 false,并且将输出参数返回 0。
  3. 针对于浮点型的取舍问题,浮点型只有 Convert.ToInt32()(int) 能进行转换,但是也是进行取舍了的:
    • Convert.ToInt32() 采取的取舍是进行四舍五入。
    • (int) 则是截取浮点型的整数部分,忽略小数部分,例如 Convert.ToInt32(1.499d)(int)1.499d 都返回 1。
    • Convert.ToInt32(1.5d) 返回 2,而 (int)1.5d 还是返回 1。
  4. 关于溢出
    • 将大的数据类型转换为小的数据类型时 Convert.ToInt32()int.Parse() 都会报溢出错误,值对于Int32 太大或太小。
    • (int) 不报错,但是返回值为 -1。

2 int 型和浮点型数据转换精度问题

2.1 float 和 double 转 int 型精度损失

floatdouble 类型的数据经过强制(显示)转换为 int 型数据,会直接截取小数的整数部分,忽略小数部分(不是四舍五入)。如:

double dblNum = 1.234;
float fNum = 1.567F;
int dblToInt = (int)dblNum;  //dblToInt = 1
int fToInt = (int)fNum;  //fToInt = 1

2.2 int 型转 float 和 double 类型精度损失

当把一个比较大的整数隐式转换为浮点类型后,会保证量级不变,但是偶尔会损失精度。这与浮点类型在计算机中的二进制存储方式有关(IEEE 745 标准)。
在这里插入图片描述

  • 编程中用到的浮点数就是 floatdouble,长度分别是 32 位(4 个字节)和 64 位(8 个字节)。
  • 浮点数是以二进制的、小数乘指数的方式存储的,底数是 2,不是 10

以 32 位的 float 为例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Eea4dxgB-1595214187055)(Note_Figures/20200408184249167_30993.png)]
由图可知,float 的存储结构是 1 个符号位,8 个指数位,23 个尾数位:

  • 符号位:表示浮点数的正或负,0 代表正,1 代表负。
  • 指数位:实际也是有正负的,但是没有单独的符号位。在计算机的世界里,进位都是二进制的,指数表示的也是2 的 N 次幂,8 位指数表达的范围是 0 到 255,而对应的实际的指数是-127 到 128。也就是说实际的指数等于指数位表示的数值减 127。这里特殊说明,-127 和+128 这两个指数数值在 IEEE 当中是保留的用作多种用途的。
  • 尾数位:只代表了二进制的小数点后的部分,小数点前的那位被省略了,当指数位全部为 0 时省略的是 0,否则省略的是 1(因为 IEEE 754标准规定小数点前留1(为的是一个数的唯一表示,且防止全是 0 浪费位数),所以既然都是 1. X X X 1.XXX 1.XXX就没必要存储这个 1. 1. 1.了)。

例, 十进制的 0.65625 转换为二进制浮点数:

  1. 先换成二进制小数:0.10101(数部分用除 2 取余的方法, 小数部分用乘 2 取整的方法)。
  2. 移位, 移到小数点前只剩 1. 1. 1. 1.0101 ∗ 2 − 1 1.0101*2^{-1} 1.010121 ( IEEE 754 标准规定小数点前只留一位且是 1,除非这个数是 0,这个过程叫规格化)。
  3. 补全位数,1 位符号位,8 位指数移码,23 位尾数:
    • 首先,第一部分符号位,因为正数为 0,所以第一位为 0。
    • 尾数位,此例中就是 1.0101 1.0101 1.0101,因为 IEEE 754 标准规定小数点前留 1(为的是一个数的唯一表示,且防止全是0浪费位数),所以既然都是 1. X X X 1.XXX 1.XXX就没必要存储这个 1. 1. 1.了 ,直接存储小数点后的数即 0101, 补全 23 位也就是 00000000000000000000101。
    • 指数位:指数用移码表示,即加上偏移量,对于 32 位的 float 偏移量是 127(二进制 01111111),64 位。 double 偏移量 1023(二进制 001111111111),此例中就是 ( − 1 ) + 01111111 = 01111110 (-1)+01111111=01111110 (1)+01111111=01111110
      把上边 3 部分连起来就得到了 32 位浮点数, 00111111000000000000000000000101 00111111000000000000000000000101 00111111000000000000000000000101

64 位的 double 的分布如下:1 位符号位,11 位指数移码(偏移量 1023),尾数 52 位,方法和上边是一样的。
在这里插入图片描述

还有一些特殊的值如下表,S 表示符号位,E 表示加偏移量之前的指数,M表示去 1. 1. 1.之前的尾数
在这里插入图片描述


☀️ 总结一下:
float 的尾数确定了浮点数的精度,而数的大小主要是靠指数位,尾数只有 23 位,加上省略的那一位便是 24 位,所以如果 int 类型的值在 2 24 2^{24} 224以内,float 是可以精确表示的,但是当超过这个数的时候就不一定能精确表示了。
🌰 举一个栗子:
首先规定8 位十进制无符号的浮点数,4 位表示指数,4 位表示尾数,统一用 0. X X X 0.XXX 0.XXX来格式化尾数,不存储 0. 0. 0.
1️⃣ 把整数 00001234 化成浮点数即 0.1234*10^4 = 0004|1234 (这里|分隔开指数和尾数)
2️⃣ 把整数 12345678 按规则转化成浮点数是 0.12345678 ∗ 1 0 8 0.12345678*10^8 0.12345678108。按照规定,指数 0008 没问题,但是尾数却超出了 4 位,只能丢失一部分尾数来近似表达。

2.3 栗子

在这里插入图片描述

private void Test()
{
    int num1 = 1234;
    int num2 = 100000001;
    float num3 = num1;  //num3 = 1234
    float num4 = num2;  //num4 = 1e8,这里损失了精度
    int num 5 = (int)num3;  //num5 = 100000000
}
private void Test()
{
    double x = 2.01;
    int y = x * 100;  // y = 200,double y = 2.01在内存中实际是 FF FF FF FF FF 1F 69 40                         
                      // 经过IEEE的标准换算过来其实是2.00999999997, 这样,乘以100后得到
                      // 200.99999997,强制转换整形的算法是向下取整,这样导致结果成了200
}
private void Test()
{
    double x = 10000000000000000000000;
    x += 1;  // x = 10000000000000000000000
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值