天天看点

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

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 = -;
    short sib = (- + ) %  - ;

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

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

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

    sia = ;
    sib = ( + ) %  - ;

    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 = ;
    unsigned short uib =  % ;
    printf("not overflow\n");
    printf("uia = %hu\n", uia);
    printf("uib = %hu\n", uib);

    uia = ;
    uib =  % ;
    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 = -;
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 = -;
    unsigned uSVal = (- - ) %  + ;
    unsigned uVal = (- - ) %  + ;

    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型最大值,这个比较奇怪了。

所以在程序设计时还是要预估数据范围来设定数据类型,以防止程序运行错误,必要时要提升数据范围。

继续阅读