有关混合数据类型转换的一些想法

来源:互联网 发布:淘宝实名认证怎么弄 编辑:程序博客网 时间:2024/05/17 01:23

有关混合数据类型转换的一些想法

一:强制数据类型转换

运用强制类型转换运算符: (数据类型)

举例:int i = (int)1.6 + (int)1.7;首先,1.6和1.7转换成整数1,然后再相加得2,最后赋值给i。

 

二:自动数据类型转换

数据类型级别(从高至低):long double > double > float > unsigned long long > long long > unsigned long > long > unsigned int > int;当long和int的级别相同时,unsigned int比long的级别高。

1.升级:从较小数据类型转换到较大数据类型

    1).赋值表达式

举例:

#include <stdio.h>

      int main(void)

      {

         char ch;

         int i;

         float fl;

 

         fl = i = ch = ‘C’;

         printf(“ch = %c, i = %d, fl = %.2f\n”, ch, i, fl);

 

         return 0;

      }

fl = i = ch = ‘C’;这是一个赋值表达式。字符’C’被赋值给变量ch,然后再赋值给变量i;最后赋值给变量fl。变量i接受由字符’C’转换的整数,按4字节存储67(字符’C’对应ASCII67);变量fl接受由字符’C’转换的浮点数,先将字符’C’转换成整数67,然后再转换成浮点数67.00,按4字节存储。

2). 函数参数的传递

举例:

#include <stdio.h>

      int main(void)

      {

           float fl = 2.3;

 

           printf(“%.2f\n”,  fl);

 

           return 0;

      }

当作为函数参数传递时,float类型被转换成double类型。在本例中,函数参数中float类型的参数fl(2.3)被自动转换成double类型,然后被读取。同样,当作为函数参数传递时,char类型和short类型被自动转换成int类型。注意,实参传递的过程,也是给变量赋值的过程。

3).混合数据类型的运算

      举例①:

           #include <stdio.h>

           int main(void)

           {

             int i = -20;

             unsigned int j = 10;

             

             printf(“%d\n”, i + j);

             printf(“%u\n”, i + j);

 

             return 0;

           }

在计算机中,有符号类型都是用补码存储的,用补码进行运算,有符号整数-20的补码为1111 1111 1111 1111 1111 1111 1110 1100。在计算表达式i + j时,其中较小的数据类型会从较小数据类型转换到较大数据类型,即int类型的变量i会转换成同变量j一样的数据类型unsigned int。按照unsigned int数据类型来读取二进制1111 1111 1111 1111 1111 1111 1110 1100,即4294967276,再与10相加,即为4294967286。

   举例②:

          #include <stdio.h>

          int main(void)

          {

              unsigned int i = 3;

              unsigned int j;

              

              j = i * (-1);

              printf(“%u\n”, j);

      

              return 0;

          }

 由于unsigned int类型级别高于int类型,所以在作乘法运算i*(-1)时,有符号整数-1将被自动转换成unsigned int类型,-1的补码为

1111  1111  1111  1111  111 1  111 1  111 1  1111,将其按unsigned int类型来读取,值为4294967295i*(-1)=3*4294967295=1288490188512884901885的二进制数为

10  1111  1111  1111  1111  1111  1111  1111  1101,取后32位,然后转换成十进制数为4294967293,也就是变量j的值。

 2.降级:从较大数据类型转换到较小数据类型

 举例:

      #include <stdio.h>

      int main(void)

{

    char ch;

    

    ch = 1107;

    printf(“%c\n”, ch);

    ch = 80.89;

    printf(“%c\n”, ch);

 

    return 0;

}

赋值语句ch = 1107;将变量ch设置为一个超出其类型范围的值,先将1107写成二进制数100 0101 0011,取后8位0101 0011,转换成char类型为‘S’。

或者可以这样计算,char类型为1字节,即8位,这8位可以表示256个字符,以256为模,将1107 % 256 = 83转换成char类型为‘S’。

当浮点类型被降级为整形类型时,原来的浮点值会被截断。在本例中,赋值语句ch = 80.89;先将浮点类型80.89截断为整数类型88,然后转换成char类型字符‘C’,再赋值给变量ch

 

三.格式化数据类型转换

根据国际标准IEEE 754,任意一个二进制浮点数V可以表示成下面的形式:


(1). 表示符号位,当S = 0V为正数;当S = 1V为负数;

(2).  M表示有效数字,大于等于1,小于2

(3). 表示指数位;

举例①:

十进制的5.0,写成二进制是101.0,相当于(-1)^0×1.01×2^2。那么,按照上面V的格式,可以得出s=0M=1.01E=2。  

举例②:

      十进制的-5.0,写成二进制是-101.0,相当于(-1)^1×1.01×2^2。那么,s=1M=1.01E=2

 

IEEE 754规定,对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M

 

                                 图一

对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M

 

                                 图二

 IEEE 754对有效数字M和指数E,还有一些特别规定:

 (1). 前面说过,1≤M<2,也就是说,M可以写成1.xxxxxx的形式,其中xxxxxx表示小

数部分。IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。32位浮点数为例,留给M只有23位,将第一位的1舍去以后,可以保存24位有效数字。

     (2). 首先,E为一个无符号整数(unsigned int)。这意味着,如果E8位,它的取值范围为0~255;如果E11位,它的取值范围为0~2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,E的真实值(即8位二进制数)必须再减去一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023

举例:

2^10E10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001,即E的真实值位10001001

其次,指数E还可以再分成三种情况:

①E不全为0不全为1。这时,浮点数就采用上面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1

②E全为0。这时,浮点数的指数E等于1-127(或者1-1023),有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。

③E全为1。这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);如果有效数字M不全为0,表示这个数不是一个数(NaN)。

对于图一,S = 0E的真实值为01111100,化为整数为124,减去127-3,即E = -3;M =1.01;V=-1的指数为0,表示V是正数,将V化为10进制数=0.15625

     1.有符号类型数格式化为无符号类型输出

       举例:

           #include <stdio.h>

           int main(void)

           {  

              int i = -1;         

     

              printf(“%u\n”,  i);

              

              return 0;

           }

变量i为有符号类型,对于有符号类型,计算机都是使用补码进行表示或者存储的,-1的补码是1111 1111 1111 1111 1111 1111 1111 1111(注:符号位不取反),将这个二进制数按无符号%u来读取,即4294967295

2.int类型格式化为浮点数类型输出

     举例:

          #include <stdio.h>

          int main(void)

          {

             int num = 9;

             flaot * pt = # // int类型和flaot类型都占用32bits

 

             printf(“%d\n”, num);            

             printf(“%f\n”, *pt);

             

             *pt = 9.0;

             printf(“%d\n”, num);

             printf(“%f\n”, *pt);

     

         return 0;   

      }

            

int类型9的二进制数0000 0000 0000 0000 0000 0000 0000 1001(对应float类型,写成

32位),对照国际标准IEEE 754S=0;指数E全为0,按照国际标准IEEE 754规定,此时的E=1-127=-126M=0.0000 0000 0000 0000 0000 0000 0000 1001。因此,将这个int类型的整数按照浮点数float类型的浮点数来读取的话,即为

V=(-1)^0×0.0000 0000 0000 0000 0000 0000 0000 1001×2^(-126)

       =1.001×2^(-146)

很显然,这是个很接近0的正数,所以,转换成浮点数就变成了0.000000

3.浮点数类型格式化为int类型输出

     *pt = 9.0;由于flaot32位,可写成V=(-1)^0×1.001×2^3,其中S=0M=1.001,E=3,转换成其真实值为3+127=130,二进制数为1000 0010;将浮点数9.0写成float类型的二进制数为0 10000010 0010 0000 0000 0000 0000 000将其按照int类型来读取,正好为1091567616 

     如果float * pt = &num改成double * pt = &num呢?

     举例:

         #include <stdio.h>

         int main(void)

         {

            int num = 9;

            double *pt = # // int类型占32bitsdouble类型占64bits

            

            printf(“%d\n”, num);

            printf(“%f\n”, *pt);

 

            *pt = 9.0;

            printf(“%d\n”, num);

            printf(“%f\n”, *pt);

 

            return 0;

         }

              

     *pt = 9.0;将浮点数9.0存储在指针pt指向的地址上,由于指针pt指向的地址只具有32bits,而double类型的地址具有64bits

     如果将double *pt = &num取消掉呢?

     举例:

          #include <stdio.h>

          int main(void)

{

   int num1 = 9;

double num2 = 9.0;

float num3 = 9.0;  

 

printf(“%d\n”, num1);

printf(“%f\n”, num1);

printf(“%d\n”, num2);

printf(“%f\n”, num2);

printf(“%d\n”, num3);

printf(“%f\n”, num3);

 

return 0;

}

     

出现了两种情况,不解。

double类型的浮点数9.0国际标准IEEE 754表示V=(-1)^0×1.001×2^3。其中S=0M=1.001;E=3,其真实值为3+1023=1026,二进制数100 0000 0010。写成二进制形式为

0 10000000010 00100000000000000000000000000000000000000000000000000将其按%d读取,截取后32bits,故打印结果为0.000000

     Float类型变量,作为参数给函数传递时,会自动被升级为double类型。

     4.栈:函数实参的传递

       举例:

           #include <stdio.h>

           int main(void)

{

        float n1 = 3.0;

        double n2 = 3.0;

        long n3 = 2000000000;

        long n4 = 1234567890;

         

        printf(“%ld %ld %ld %ld\n”, n1, n2, n3, n4);

 

        return 0;

}

        

printf(“%ld %ld %ld %ld\n”, n1, n2, n3, n4);该调用将n1,n2,n3,n44个参数传递给函数,并且计算机把传入的值根据其变量的数据类型,而不是根据转换说明的数据类型,把这些值放入栈中。因此,n1被存储在栈中,占64bits;同样,n2也在栈中占用64bits;而n3n4在栈中分别占用了32bitsprintf()函数根据转换说明,而不是根据传入的值的数据类型,从栈中读取。%ld转换说明表示printf()函数应该读取32bits,所以,printf()函数读取了栈中前32bits作为第一个值。然而,这是n1的前半部分,被解释成了一个long类型的整数。printf()函数根据下一个%ld转换说明,printf()函数再读取32bits。这是n1的后半部分,也被解释成了一个long类型的整数。类似的,根据第3个和第4%ldprintf()函数读取了n2的前半部分和后半部分,并解释成了2long类型的整数。

    综上,由第2条,第3条和第4条可以看出,用指针取地址上的值时,会根据指针指向的数据类型取地址的长度,例如float *pt;取回float类型的32bits地址的值;double *pt;取回double类型的64bits地址上的值。而对于基本变量float a;double b;在作为参数传入函数时候,直接都被自动升级为了double类型的数据类型,都是64bits

原创粉丝点击