【计组笔记-5】详细测试c++中类型转换的溢出截断处理


  1. 测试环境是vs2019。
  2. 会尽量把能想到的情况都测试&分析一下,有想不到的还望批评指正。

1. 将小数赋值给float

明天写(记得解释精度丢失)


先跳过吧时间太赶了…我先写点别的,其他几篇笔记已经把原理讲完了,这篇只是测试,过几天再写()。

2. 将整数赋值给float

3. 将整数赋值给int

3.1 算术溢出

如果通过算数运算给变量赋的值大于数据类型能容纳的最大值,则会警告算术溢出,但不会报错,如下所示:

int i = 0x7fffffff+1;

在这里插入图片描述
来分析一下,已知有符号int为32位,其中1位是符号位,31位是数值位,因此,理论上int可储存的最大值为231-1,用16进制表示为0x7fffffff,而理论上最小的溢出值为0x7fffffff+1,也就是i。

溢出之后,i储存了什么内容呢?打印出来看一下:
在这里插入图片描述
翻译成储存的二进制位是1000 0000 0000 0000 0000 0000 0000 0000(理论上是-0,由于不需要存-0,因此这个状态被拿来存-231了,笔记-3中有写),可以发现,如果将其视为无符号数,则刚好是0x7fffffff+1的值。

可见,在这个赋值过程中,编译器是截断32位赋值给左侧int型变量的,并不会因为int有一位是符号位就只截断31还给补足符号位再顺便转换成补码, 而如果只赋值0x7fffffff呢:

int i1 = 0x7fffffff;
int i2 = -0x7fffffff;

在这里插入图片描述
可见,只要右侧的数值只需要低于32位的内容来储存,最高位就可以正确的储存符号位,且储存的也是正确的补码(补码后面的笔记再写,这里不多赘述)。

3.2 非算术溢出(等于32位)

上一小节分析了算术溢出,那么如果是非算术溢出,即直接赋一个超过最大值的数呢?如下所示:

int i1 = 0xffffffff;
int i2 = 0x80000000;

在这里插入图片描述
注意,由于右侧的值并未超过32位,因此这时候是不会报任何错或警告的!此外,打印出来的i1和i2,分别翻译成储存的二进制内容,如下所示:

  1. i1:1111 1111 1111 1111 1111 1111 1111 1111
  2. i2:1000 0000 0000 0000 0000 0000 0000 0000

可见,如果右侧赋值的数大于int能储存的最大值,则这个储存过程是截断32位直接存给int变量指示的那块内存,而并没有添加符号位,再修改为补码这个过程。然而,这个问题是不会报错的,连警告也无!所以还是要自己注意下别超了。

3.3 非算术溢出(大于32位)

上一小节分析了赋的值用32位装得下的问题,那么,如果大于32位,赋值给int会如何截断呢?如下所示:

int i1 = 0x100000000;
int i2 = 0x100000001;
int i3 = 0x180000003;

在这里插入图片描述
根据打印的值,i1,i2,i3实际储存的二进制位如下:

  1. i1:0000 0000 0000 0000 0000 0000 0000 0000(0x00000000)
  2. i2:0000 0000 0000 0000 0000 0000 0000 0001(0x00000001)
  3. i3:1000 0000 0000 0000 0000 0000 0000 0011(0x80000003)

如果观察一下,就可以发现,它们刚好就是从低位开始截取了所赋值的32位,除此之外并没有其他改变。

此外,跟上一小节不同的是,在赋的值需要大于32位来储存时,会报以下警告:
在这里插入图片描述
可见确实是截断操作,此外还可以获得一个信息,这里右侧的数字是用64位int来存的(不知是否与操作系统是64位有关),在小数里也有这个现象(在小数的小节里分析)。

4. 将小数赋值给int

测试如下赋值方式:

int i1 = 0.9;
int i2 = 0x80000000 + 0.9;
int i3 = 0x100000000 + 0.9;
int i4 = 0x180000003 + 0.9 * 2;
int i5 = 0x180000003 + 0.9 + 1;
int i6 = -0x7fffffff + 0.9;
int i7 = -0x7fffffff - 0.9;

在这里插入图片描述
它们报的警告也都是一样的,如下:
在这里插入图片描述
可见,虽然i2,i3,i4与i5的赋值过程中出现了第3部分中分析过的问题,如算术溢出,但如果涉及到了类型转换,则只会提示类型转换的警告

根据打印的值,i1,i2,i3实际储存的二进制位如下:

  1. i1:0000 0000 0000 0000 0000 0000 0000 0000(0x00000000)
  2. i2:1000 0000 0000 0000 0000 0000 0000 0000(0x80000000)
  3. i3:0000 0000 0000 0000 0000 0000 0000 0000(0x00000000)
  4. i4:1000 0000 0000 0000 0000 0000 0000 0100(0x80000004)
  5. i5:1000 0000 0000 0000 0000 0000 0000 0100(0x80000004)
  6. i6:1000 0000 0000 0000 0000 0000 0000 0010(0x80000002)
  7. i7:1000 0000 0000 0000 0000 0000 0000 0001(0x80000001)

分析打印结果可知,将小数赋值给整数,计算后的小数点部分会被抛弃,并且,这个抛弃是发生在全部计算完之后的,可以理解为右侧的表达式计算得出了类型为double的结果,然后再将这个double类型的数赋值给int。

此外,在抛弃掉小数点后的内容之后,剩余整数部分的处理与上一节是一样的,大于等于32位则直接截断(如i2,i3,i4,i5),小于32位就按照正常流程转为补码储存(i6,i7)。(然而,这里并不会提示进行了截断)

5. int和short互相转换

6. double和float互相转换

7. 总结

  1. 敲代码的时候要更加注意溢出这个问题,有时溢出了连警告都没有。
  2. c++好歹还给了明确的数据类型,js这种解释型语言只有number,估计问题更大,有机会要好好测试一波。
  3. 本篇只来及测试了部分类型和部分转换情况,后续时间充裕会慢慢补全。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值