搜索
查看: 2771|回复: 16
打印 上一主题 下一主题

[简短的教程] 为什么说”你可能从来不需要float" ?

[复制链接]
跳转到指定楼层
楼主
发表于 2014-3-20 00:20:27 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
因为float经常能给你一些惊喜

例如论坛上常常看到的
  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. int main()
  4. {
  5.   float a, b, c;
  6.   a = 5.2;
  7.   b = 3.1;
  8.   c = a+b;
  9.   printf("%f\n", c);
  10.   system("pause");
  11.   return 0;
  12. }
复制代码


输出是8.299999,这明显不正确

有人会教你这么做
  1. printf("%.1f\n", c);
复制代码

输出是变成8.3了,但是%.1f是什么?
是进行近似处理,保留一位小数

所以说,%.1f对于解决这个问题根本没有帮助,输出的是近似后的结果,如果需要精确结果呢?
如何让计算机给出精确的结果才是我们想要的,而不是一个看上去正常的结果

这里的原因在于,二进制小数和我们熟悉的十进制小数有很大的不同。
一个最简分数,如果分母进行质因数分解后仅含2和5,那么这个分数就能写成有限小数(因为10的质因子是2和5)
但是对于二进制小数来说,规则就变了。很多有限小数转换成二进制,是没办法写成有限小数的,上面的8.3就是这么一个例子。
没法写成有限小数,就只能写出一部分小数位来近似了(例如把1/3近似成0.333333)

所以说,二进制小数是注定不精确的。
float呢,特别不精确,它只能保证6位的有效数字。(注意,是有效数字,不是小数点后位数。1000.001有7位有效数字,0.001有1位有效数字,定义可以自己去查,想必自己动手查要比在这里转述一句效果好)
特别在整数位上有数字的时候,用float的结果就是灾难性的:
  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. int main()
  4. {
  5.   float a, b, c;
  6.   a = 50000000.2;
  7.   b = 3.1;
  8.   c = a+b;
  9.   printf("%f\n", c);
  10.   system("pause");
  11.   return 0;
  12. }
复制代码

输出是50000004.000000,0.7就这样被吃掉了。
可能会有人觉得0.7相对于整个数字来说微不足道,但是如果是银行的结算程序,银行是肯定不愿意亏这7毛钱的。

为什么会这样呢?重复前面的一句话,float仅有6位有效数字,从左边开始数6个数字,确实是正确的。

看到这里你可能就会明白了,float是一种”没用的东西“(引号去掉大体上也是可以的)
推荐
发表于 2014-3-20 10:05:47 | 只看该作者
很给力!回头看.

点评

当心你的脖子断了  发表于 2014-6-14 11:47
沙发
 楼主| 发表于 2014-3-20 00:35:18 | 只看该作者
本帖最后由 rosynirvana 于 2014-3-20 00:54 编辑

那么如果要用到小数,该怎么办呢?

1. 用整数来模拟
典型的例子是自用的流水账程序。这种程序只要考虑收入多少支出多少就行了,人民币的最小单位是分,如果我们用分来做单位,就不会出现小数了。
整数会不会出现位数不够用的情况? 对当前的编译器来说,int一般是32位,上限是大约21亿,减去小数点后的两位,能统计到两千万而不出错。
如果确实不够用,那现在的编译器一般都支持long long int, 上限足以放得下一国的GDP。

  1. #include <stdio.h>

  2. int sum;

  3. int main()
  4. {
  5.     puts("For poor people only!");
  6.     puts("Non-digit input will print the balance");
  7.    
  8.         while(1){
  9.                 int current = 0;
  10.                 if(scanf("%d", &current) == 1){
  11.                         sum += current;
  12.                 printf("%+d\n", current);
  13.                 }
  14.                 else{
  15.                         int ch = 0;
  16.                         printf("balance: %d\n", sum);
  17.                         while((ch = getchar()) && ch == '\n')
  18.                                 ;
  19.                 }
  20.     }
  21.   return 0;
  22. }
复制代码


2. 用double
double要比float精确很多。
就像表示1/3这个分数,用0.33333333333要比0.333333精确很多一个道理。
double有时候也会出现问题(毕竟是近似),但是要比float少很多

那么有没有可能,在某种情况下用float是最好的?
有的,float占的内存毕竟要少,例如一个模拟系统中
  1. typedef struct { float a; float b; float c } point;
  2. point arr[100000];
复制代码

point结构体用来记录空间中一个点的坐标,如果我们需要记录很多点的坐标而且对坐标精度要求不高,那么用float就可能节省很多内存(当前通常一个float 4字节,一个double8字节,上面的例子用float可以节省(8-4) x 3 x 100000的内存)。

但是,现在的内存真的很便宜啊……
但是,就算你写程序不浪费内存,很多别的程序还是会浪费内存啊……
但是,这种模拟系统也不常见啊……

运行速度不用担心,也是double比较快……而且如果你用比较高级点的语言,编译器可能对于double有专门的优化,速度优势就更明显了。

看到这里,你觉得你还需要float这东西吗?
地板
发表于 2014-3-21 11:52:25 | 只看该作者
顶一个!!!
5#
发表于 2014-3-23 12:10:04 | 只看该作者

给力!!话说为什么你总是说回头看。。、。、。、

点评

估计前头已经看过了...  发表于 2014-8-1 01:36
6#
发表于 2014-3-23 15:06:33 | 只看该作者
虽然还没有学到如此高深的程度,留一脚印以做纪念,慢慢消化
7#
发表于 2014-3-23 20:39:24 | 只看该作者
cad20020601 发表于 2014-3-23 12:10
给力!!话说为什么你总是说回头看。。、。、。、

没时间啊!                                 
8#
发表于 2014-5-27 20:48:06 | 只看该作者
:L:L:L:L
9#
发表于 2014-6-13 22:23:30 | 只看该作者
Mark!
10#
发表于 2014-8-28 12:15:39 | 只看该作者
:L:L:L:L:L:L:L:L:L:L:L:L:L:L:L:L:L:L:L:L:L:L:L:L:L:L:L:L
11#
发表于 2015-2-14 13:20:33 | 只看该作者
暂时还不能全部看懂,我会把我的问题一一的发帖提问,等到我把整本书学完之后再回来看问题应该会明白很多的吧。
12#
发表于 2015-2-23 21:49:16 | 只看该作者
犀利。。。
13#
发表于 2015-4-9 15:34:06 | 只看该作者
学习长知识了
14#
发表于 2015-10-24 22:31:39 | 只看该作者
我在回顾rosynirvana大神的旧帖
15#
发表于 2017-8-1 16:06:16 | 只看该作者
哇塞 ,学到了!谢谢!!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

广播台
特别关注
快速回复 返回顶部 返回列表