C中double到int的转换、四舍五入

来源:互联网 发布:菜鸟手机助手源码 编辑:程序博客网 时间:2024/05/16 12:00

C中double到int的转换、四舍五入

原创 2014年06月01日 17:02:13
  • 编辑
  • 删除

在一般的PC平台下sizeof(double)=8,即它是8字节的,同时它是双精度浮点型,而float是单精度的。先把它们的基础知识复习一下,有些有点忘了。

1.double类型的数在C中的正确输入、输出怎么表示?

%lf表示双精度,而%f表示单精度。

2.浮点类型数据在内存中的表示

在没有认真学习之前的一种错误认识:认为64位的浮点数,有一部分用于表示整数部分,一部分用于表示小数部分。之所以是错误的是因为没有理解浮点所表示的意思:小数点的位置位置根据需要而浮动。即小数点的位置不是固定的。

该类型数据在内存中表示方式与整型的不同。它采用科学计数法的方式表示,即内存中由符号域、小数域和指数这三部分组成(但这三个域在内存中具体占多少位,并没有统一的规定,与不同平台有关)。如3.424在内存中的表示就类似如下:

+  .34241符号小数域指数域
这里假设指数是10的幂,从而3.424=0.3424*10^1
但实际PC中小数域和指数域都是用二进制表示的,一般也以2的幂表示。
在Torbo C中float为32bit,而有效的数字只有6-7个。而double的有效数字有15-16个
[objc] view plain copy
print?
  1. /* Note:Your choice is C IDE */  
  2. #include "stdio.h"  
  3. void main()  
  4. {  
  5.     float a,b;  
  6.     int d=123456789;   
  7.     int d1;  
  8.     a=123.456781e5;//12345678.9  
  9.      d1=(int)a;  
  10.    b=a+0.4;  
  11.     printf("a=%f\n",a);  
  12.      printf("b=%f\n",b);  
  13.     printf("d=%d\n",d);  
  14.    printf("d1=%d\n",d1);  
  15.       
  16. }     
注意:float为32bit,而有效的数字只有6-7个,因此a=123.456789e5;//12345678.9,从7之后就是不确定的了。
  此外编译器会提示:e: warning C4305: '=' : truncation from 'const double' to 'float'对应红色部分。
在默认情况下,4.554等小数表示为double类型的。
[objc] view plain copy
print?
  1. b=a+0.4,也是先用double运算,再把double结果给float赋值。  
double类型给float\int等类型赋值时可能发生精度损失问题。
[objc] view plain copy
print?
  1. a=12345678.000000  
  2. b=12345678.000000  
  3. d=123456789  
  4. d1=12345678  
  5. Press any key to continue  

3. 四舍五入的情况

3.1把float\double按格式,限制小数点个数时会发生四舍五入。
3.2把float\double强转化为int类型时,只取整数部分不会四舍五入

[objc] view plain copy
print?
  1. #include "stdio.h"  
  2. void main()  
  3. {  
  4.     float a,b;  
  5.     int d,d1;  
  6.     a=3.56;// warning const double to float  
  7.     b=(float)3.34;//这样就不警告了。  
  8.       
  9.     printf("a=%f,b=%f\n",a,b);//默认情况下,小数点6位  
  10.     d=(int)a;//只取整数部分而不会四舍五入。  
  11.     d1=(int)(b);//只取整数部分而不会四舍五入  
  12.     printf("d=%d,d1=%d\n",d,d1);    
  13.     printf("m.n的格式会四舍五入:%.1f,%.1f",a,b);      
  14. }     
[objc] view plain copy
print?
  1. a=3.560000,b=3.340000  
  2. d=3,d1=3  
  3. m.n的格式会四舍五入:3.6,3.3Press any key to continue  
 a=1234567.45443;
    printf("a=%f,b=%f\n",a,b);//默认情况下,小数点6位
,a为1234567.500000,即后面的精度都是不可靠的

3.3 如何把float或double给int赋值时,也按四舍五入的方式。即:3.4--〈3〉 4.5--〈5〉

可以采用sprintf,(奇怪在VC下没有snprinf))
[objc] view plain copy
print?
  1. float a,b;  
  2.    int d,d1;  
  3. char buf[14];  
  4.    a=(float)3.56;  
  5.    b=(float)3.34;//这样就不警告了。  
  6.   
  7. sprintf(buf,"%.0f",a);  
  8. d=atoi(buf);  
  9. sprintf(buf,"%.0f",b);  
  10. d1=atoi(buf);     
  11.    printf("d=%d,d1=%d\n",d,d1);     
  12. return 0;  
[objc] view plain copy
print?
  1. d=4,d1=3  
  2. Press any key to continue  

3.4 如何提取小数点部分

利用到int的强制转换就只提取整数部分,若利用floor函数,看3.5
[objc] view plain copy
print?
  1. double df=3.4564233;  
  2. double df1=3.6564235;  
  3. int d;  
  4. printf("%lf\n",df);//double也默认只有6个小数  
  5. printf("%.7lf\n",df1);//可以完整输出。  
  6. d=(int)df1;//只取整数部分  
  7. df1=df1-d;  
  8. printf("%.7lf\n",df1);  

[objc] view plain copy
print?
  1. 3.456423  
  2. 3.6564235  
  3. 0.6564235  
  4. Press any key to continue  

3.5 ceil \floor

在一些算法或运算中可能要用到四舍五入、向上取整┌X┐、向下取整等操作.└X┘
其中ceil()函数是天花板的意思,即向上取整。floor为地板,即向下取整。
[objc] view plain copy
print?
  1. #include <math.h>  
  2. double floor( double arg );  
  3. 功能: 函数返回参数不大于arg的最大整数。  
  4. ___________________________________  
  5.   
  6. double ceil(double x);    
  7. 功 能: 返回大于或者等于指定表达式的最小整数  

它们的参数都为double类型的,但我们经常会用int类型的数据进行操作。
如下完全二叉树中,若以1为根结点,则4,5这两个节点的双亲为[4/2] [5/2]都为向下取整。
[objc] view plain copy
print?
  1. int a=4;  
  2.     int b=5;  
  3.     int par;  
  4.     par=(int)floor(a/2);  
  5.     printf("a's parent index is:%d\n",par);  
  6.     par=(int)floor(b/2);  
  7.     printf("b's parent index is:%d\n",par);  

[objc] view plain copy
print?
  1. a's parent index is:2  
  2. b's parent index is:2  
  3. Press any key to continue  
对负数的处理,还是按数学上的大小进行的。即floor(-4.6)=-5. ceil(-3.4)=-3;
[objc] view plain copy
print?
  1. double a=4.67;  
  2. double b=-4.67;  
  3. double c=4.23;  
  4. double d=-4.23;  
  5. double a_floor=floor(a);  
  6. double b_floor=floor(b);  
  7. double c_floor=floor(c);  
  8. double d_floor=floor(d);  
  9. double a_ceil=ceil(a);  
  10. double b_ceil=ceil(b);  
  11. double c_ceil=ceil(c);  
  12. double d_ceil=ceil(d);  
  13.   
  14. printf("a_floor=%lf,b_floor=%lf\n",a_floor,b_floor);  
  15. printf("c_floor=%lf,d_floor=%lf\n",c_floor,d_floor);  
  16. printf("a_ceil=%lf,b_ceil=%lf\n",a_ceil,b_ceil);  
  17. printf("c_ceil=%lf,d_ceil=%lf\n",c_ceil,d_ceil);  
[objc] view plain copy
print?
  1. a_floor=4.000000,b_floor=-5.000000  
  2. c_floor=4.000000,d_floor=-5.000000  
  3. a_ceil=5.000000,b_ceil=-4.000000  
  4. c_ceil=5.000000,d_ceil=-4.000000  
  5. Press any key to continue  

4.若double变量大于int所存储的范围时,强制转换的结果是不确定的

[objc] view plain copy
print?
  1. double a=179723554568;  
  2.     int d=(int)a;  
  3.     printf("a=%lf\n",a);  
  4.     printf("d=%d\n",d);  
  5.     return 0;  

[objc] view plain copy
print?
  1. a=179723554568.000000  
  2. d=-665071864  
  3. Press any key to continue  

int 32位时表示的范围为(-2^31--(2^31-1))即-2147483648,2147483647
2147483647=(0111 1111 1111 1111 1111 1111 1111 1111)
-2147483648= 1000 0000 0000 0000 0000 0000 0000 0000
[objc] view plain copy
print?
  1. double a=2147483647+1;  
  2. double a2=2147483648;//注意a2!=a  
  3. int d=(int)a;  
  4. int d2=(int)a2;  
  5. printf("a=%lf,a2=%lf\n",a,a2);  
  6. printf("d=%d,d2=%d\n",d,d2);  
  7. return 0;  
[objc] view plain copy
print?
  1. a=-2147483648.000000,a2=2147483648.000000  
  2. d=-2147483648,d2=-2147483648  
  3. Press any key to continue  
注意:a 并直接赋值,由于像1,2,344等整数,默认情况下是int类型的,而
2147483647能够用int类型存储,因此它还是按int类型计算的,但此时发生了溢出,从而它符号位变为1了,溢出后为最大的负数,再把这个负数赋值给double.
2147483648,则超出了int的范围,则上升为long int。1000 0000 0000 0000 0000 0000 0000 0000


注意发生溢出后,是变为负的最大,而不是变为0.
0的二进制全为0,
-1的二进制全为1.
[objc] view plain copy
print?
  1. char c=128;  
  2. int dd=c;  
  3. printf("dd=%d\n",dd);  

[objc] view plain copy
print?
  1. dd=-128  
  2. Press any key to continue  



[objc] view plain copy
print?
  1. char c=352;//1 0110 0000  
  2. int dd=c;  
  3. printf("dd=%d\n",dd);  
结果为它的低8位,为96_________________________________________________________________________________________________________________________

5.19.9变成了19.89999

在十进制中小数有些是无法完整用二进制表示的。它们只能用有限位来表示,从而在存储时可能就会有误差。十进制的小数采用乘2取整法进行计算,取掉整数部分后,剩下的小数继续乘以2,直到小数部分全为0.

如0.125变成二进制为

0.125*2=0.25  .....取整0

0.25*2=0.5 ........取整0

0.5*2= 1.0 ………取整1

0.0*2=0

所以0.125的二进制为0.001


如我们有
而0.9*2=1.8.....取整1
0.8*2=1.6…....取整1
0.6*2=1.2.......取整1
0.2*2=0.4........取整0
0.4*2=0.8...取整0
0.8*2=1.6....取整1
………………………………
从而它是一个循环,不可能出现小数部分为0的情况。从而在内存中表示时就会小于0.9
[objc] view plain copy
print?
  1. double a=19.9;  
  2.     int b=(int)(19.9*100);  
  3.     printf("b=%d\n",b);  
  4.     printf("a=%lf\n",a);  
[objc] view plain copy
print?
  1. b=1989  
  2. a=19.900000  
  3. Press any key to continue  

采用
采用VC6.0可以观察到a的实际并不是19.9(F9设置断点,F5执行)

a19.899999999999999
b1989
printf("%lf\n",a)中由于发生了四舍五入才会变成19.9。
但19.9*100是按二进制乘法进行运算(而不是我们十进制)的,而(int)的强制又只取整数部分,它不会四舍五入。

但19.9*10=199

[objc] view plain copy
print?
  1. double a=19.9;  
  2. int b=(int)(19.9*10);  
  3. printf("b=%d\n",b);  
  4. printf("a=%.6lf\n",a);  
  5.   
  6. return 0;  

[objc] view plain copy
print?
  1. b=199  
  2. a=19.900000  
  3. Press any key to continue  
VCwatch中观察到的为
a19.899999999999999
b199


个人觉得所得的结果与乘以19.9的那个数是有关的,在二进制中,用不同的数进制运算,所得的误差可能会变放大,如
(a+b)*c=ac+bc,这里假设b为误差,则它被放大了c倍,而bc与它相加则可能在某个特殊的地方就产生了致使的错误。
如果认真去算的话,要以二进制去推导。
这个问题的发现是在do_and_want中的博客看到的,背景是这样的:
某商品的定价为19.9元,由于在数据处理过程中把所有的端口转化成整数处理(商品价格只有到分),所以把它乘以100后再进行处理,从而意思地发现商品少了一分钱。若数量大的话,损失也是可观的。
下面是来看网络中的分析:

do-and-want

19.9 作为 Double 类型表示,二进制形式是:
1 00000000110 011111001100110011001100110011001100110011001100110
(注意中间的两个空格,如果你不知道啥意思,就去查查double的内存表示形式吧)
但是19.9 * 100 由于是二进制运算的结果是
1 00000010011 111000101111111111111111111111111111111111111111111
由于后面有n个11111所以我猜测可能发生了溢出被计算机舍去了.
于是这个数字比 1990少那么一点点(可能是 1989.99999999...)
但是你的取整操作却直接截断了后面的数字,于是成了1989
至于你说9.9 29.9为什么不那样,那就是可能没有发生溢出了(不要以10进制的思维来猜测二进制)
别的语言你只能通过保留更高的精度并且四舍五入来实现,而C#为了支持金融运算,独家引入变态的Decimal类型,于是你的问题现在可以通过decimal解决了(decimal的精度非常高,大约有好几十层楼那么高吧...够用了)



转载请标明出处:http://blog.csdn.net/lin200753/article/details/27952897

do-and-want


原创粉丝点击