为什么编程语言会存在精度丢失问题?

tpwonline
  • 520
精度丢失问题:
$f = 0.57;
echo intval($f * 100);  //56

比如上述的例子,如果按常规理解来看,应该得到的结果是0.57,然而转成整型之后,得到的结果却是56。
既然大家的常规理解都认为结果应该是57,为啥编程语言不在底层把这个问题给处理掉,而是让开发人员用高精度函数自行解决?
难道是高精度函数运行速度慢?还是说低精度计算在某种使用场景里是很有必要的?

回复
阅读 1.2k
5 个回答
✓ 已被采纳

初学者往往会认为:整数就是int,小数就是float。

其实你查字典就知道,float的含义不是小数:

float,英语单词,动词、名词,作动词时意为“使漂浮,浮动;漂流,飘动;飘移;安排(贷款)提出,提请考虑(想法或计划);发行(股票)上市;(货币汇率)自由浮动实行”,作名词时意为“(酒吧等用于给顾客找零的)备用零钱;彩车,花车;浮板;漂浮物;鱼漂;浮子;加冰激凌的饮料;浮动期;坐浮箱(治病、疗伤或放松);救生圈; (Float)(美、英、德)弗洛特(人名)”。

这跟小数完全八杆子打不着啊,那为什么大家会习惯性地以为float就是小数呢?其实float是浮点数,只是小数表示方法的一种。除此之外,小数还可以表示为定点数。浮点数就是漂浮嘛,漂浮就是上下起伏不定嘛,当然就会存在精度缺失问题。你要想精确,就应该用定点数来计算,用定点数表示的小数不存在精度缺失问题。

任何语言都有相应的定点数表示和计算方法。拿PHP来说,你可以用BC库来计算,比如我们想算个减法:

<?php
$a = '1.234';
$b = '5';
echo bcsub($a, $b, 3);  // -3.766
?>

分毫不差,不可能有缺失。

至于浮点数为什么会有精度缺失问题,想了解具体原理,网上有很多类似文章,感兴趣的可以去自学,我就不解释了。

总之,玩小数,如果你想要确定的精确结果,那你就用定点数,不要用浮点数。(当然,定点数也不是万能的,它有自己的适用范围,具体可以自行百度。我的经验是:对于一般的常规运算,定点数已经足够,除非你要计算天文数字或者量子物理)

浮点数的存储方式注定了有精度损失

你的这个相当于

double x = 0.57;
int y = (int)(x * 100);

结果是你自行选择的,"编程语言的底层"不能去猜测你的意图。

换成这样呢:

$f = 0.57;
echo floatval($f * 100);  
lavyoung
  • 2
新手上路,请多包涵

因为计算机底层是用二进制存储数据,十进制数据转二进制存储时有些数字不能完全转换,只能无限接近于原本的值,这就会导致在后面的运算会出现不正确结果

不是精度的问题,是进制的问题。

我们通常用十进制,但是计算机里一般都是二进制。很多十进制小数在二进制里是无法精确表示的。这就和十进制小数无法精确表示 1/3 一样。

无论是十进制小数,还是二进制小数,都无法精确表示所有的有理数。而在现在的计算机里,二进制小数真的比十进制小数算得快得多。所以大家都用二进制,都有精度问题。

如果一定要精确怎么办呢(比如在涉及到钱的计算的时候),其实很多语言都有类似 decimal 的类型,可以精确表示所有十进制小数。有的还有 rational 类型,精确表示所有有理数。可以按需使用。他们是一定比普通的 float 慢很多的。

木原金
  • 50

经典的 0.1 + 0.2 = 0.30000000000000004 问题
0.30000000000000004.com

本质上在于,N进制的小数只能完整表示质因数在以N的质因数范围以内的分母的分数,如10进制小数可以完整表示 1/2、1/4 、1/5、1/8、1/10...,因为它们的分母的质因数都属于{1,2,5}。而二进制的质因数只有{1,2},故十进制中能完整表示的1/5、1/10...等分数在二进制中无法完整表示。

所以变量X在赋值时就已经是循环小数了

这根是不是浮点数和定点数没有关系,字符串表示的十进制小数毫无疑问是有限小数。

宣传栏