欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C\C++中int、double等数据类型范围的理解

程序员文章站 2022-07-15 09:56:53
...

C \ C++中整型数据都是有数据范围的,对溢出的数据处理机制一般是数据值和数据范围值进行求模处理,求模可以这么理解,数据描述是一个数据描述范围最小值到最大值的一个闭环循环,求模后的值会仍在这个闭环范围内,例如钟表,可用1~12来一个闭环来描述,13则就是从新回到1(13 % 12)。
在C\C++中,一个整型数据val,其当前类型下的描述值可用如下公式计算:
描述值 = (val - 当前类型最小值) % 数据范围 + (当前类型最小值)
这个计算对于数据溢出和不溢出都是适用的。
以short 和 unsigned short类型来说明。

(1)short int数据类型
short数据占用2个字节,则数据范围为-32768~32767,共计65536个数,则一个short val,其实际描述值realVal为
realVal = (val - (-32768)) % 65536 + (-32768)

示例代码:

    short sia = -32760;
    short sib = (-32760 + 32768) % 65536 - 32768;

    printf("not overflow\n");
    printf("sia = %hd\n", sia);
    printf("sib = %hd\n", sib);

    sia = -32769;
    sib = (-32769 + 32768) % 65536 - 32768;

    printf("\noverflow\n");
    printf("sia = %hd\n", sia);
    printf("sib = %hd\n", sib);

    sia = 32768;
    sib = (32768 + 32768) % 65536 - 32768;

    printf("\noverflow\n");
    printf("sia = %hd\n", sia);
    printf("sib = %hd\n", sib);

运行结果:
C\C++中int、double等数据类型范围的理解

说明:
(1)在赋值操作时,如sia = -32769,-32769是先作为一个常量值存放在内存中,这个数值是没有越界的概念的,就是二进制值,只是在赋值操作时才进行了对应的数据转换;
(2)从运行结果可见,该处理机制对数据溢出和不溢出得到的结果都是适用的;
(3)联想数据闭环循环,如程序中32768,超过short型上限,其溢出一个数,作为一个数据闭环,则直接跳转到了-32768,也就是short型下限值。

(2)unsigned short数据类型
处理机制同上,对于一个unsigned short数据val,实际存储值realVal为
realVal = (val - 0) % 65536 + 0

示例代码:

    unsigned short uia = 776;
    unsigned short uib = 776 % 65536;
    printf("not overflow\n");
    printf("uia = %hu\n", uia);
    printf("uib = %hu\n", uib);

    uia = 65539;
    uib = 65539 % 65536;
    printf("\noverflow\n");
    printf("uia = %hu\n", uia);
    printf("uib = %hu\n", uib);

运行结果:
C\C++中int、double等数据类型范围的理解

(3)double和float的数据精度
double和float数据有精度范围,对于float和double类型的精度范围和描述方式,可参考下面的两篇文章:

http://www.linuxidc.com/Linux/2012-07/65987.htm
http://blog.sina.com.cn/s/blog_6ebd49350101gdgo.html

一旦超过精度范围,就不能精确的描述该数据,不同的处理器对不能精确描述的部分的处理机制可能是不同的,这也就导致了在不同的平台上为什么一套代码的运行结果会不一致,最近在调试程序时,出现PC上(intel处理器)和手机中(arm处理器)中运行结果不一致的现象。在单步调试时发现在超出数据精度后的处理机制不一致而导致的。

(4)数据类型转换
当unsigned型数据类型之间转换时,低范围向高范围转换时不会出现溢出现象,高范围向低范围转换时,则可以按照上述方式得到要转换的值。

当unsigned和signed类型之间进行转换时,正数部分的转换同上,由负数转向unsigned型,也就是直接读取内存中的数据,然后进行格式转换即可,例如

short val = -1;
unsigned short uval = val;
unsigned int uival = val;

数据存储是以反码表示,short型-1的表示为1111 1111 1111 1111(补码形式),转换为unsigned short时不需要扩展字节,直接读取字节内容即为1111 1111 1111 1111,这里没有符号位,则uval = 65535,同理转换为unsigned int时,需要扩展字节,为了尽量避免转换错误会先进行同类型数据转换,即先转成int,然后在转成unsigned int,有如下处理过程:

int temp = val;
unsigned short uval = temp;

转成int时会扩展字节,扩展为0xFFFFFFFF,在转换为unsigned short时,则uval = 4294967295。
上述转换也可以用上面的公式来计算得到
示例代码:

    short iSVal = -1;
    unsigned uSVal = (-1 - 0) % 65536 + 0;
    unsigned uVal = (-1 - 0) % 0x100000000 + 0;

    printf("iSVal = %hd\n\n", iSVal);
    printf("iSVal change unsigned short val %hu\n", iSVal);
    printf("uSVal = %hu\n", uSVal);

    printf("\niSVal change unsigned int val %u\n", iSVal);
    printf("uVal = %u\n", uVal);

运行结果:
C\C++中int、double等数据类型范围的理解

可见在进行类型转换时均可以使用上面的公式来得到结果!不过不建议unsigned和signed之间进行类型转换。

(5)其他
不同处理器之间可能对数据越界问题的处理机制可能会不一样,前面说过arm和intel对float和double型数据超过其精度范围的数据处理机制就不同,同样在调试中发现arm处理器对int数据越界后会将值设定为int型最大值,这个比较奇怪了。
所以在程序设计时还是要预估数据范围来设定数据类型,以防止程序运行错误,必要时要提升数据范围。