C浮点精度
可能重复:
浮点比较
我对C / C ++中float的准确性有疑问。 当我执行以下程序时:
#include int main (void) { float a = 101.1; double b = 101.1; printf ("a: %f\n", a); printf ("b: %lf\n", b); return 0; }
结果:
a: 101.099998 b: 101.100000
我相信浮点数应该是32位所以应该足以存储101.1为什么?
如果它们可以通过将两个的反转幂加在一起构建(即, 2 -n
像1
1/2
1/4
,那么,您只能在IEEE754中精确表示数字(至少对于单精度和双精度二进制格式)) 1/65536
等等)取决于可用于精度的位数。
在浮点数(23位精度) 或双精度(52位精度)提供的缩放范围内,没有两种反转功率的组合可以精确到101.1。
如果你想要一个快速教程,了解这种反向function的两种function是如何工作的,请参阅这个答案 。
将该答案的知识应用于您的101.1
号码(作为单个精度浮点数):
s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm 1/n 0 10000101 10010100011001100110011 | | | || || || |+- 8388608 | | | || || || +-- 4194304 | | | || || |+----- 524288 | | | || || +------ 262144 | | | || |+--------- 32768 | | | || +---------- 16384 | | | |+------------- 2048 | | | +-------------- 1024 | | +------------------ 64 | +-------------------- 16 +----------------------- 2
它的尾数部分实际上永远持续101.1
:
mmmmmmmmm mmmm mmmm mmmm mm 100101000 1100 1100 1100 11|00 1100 (and so on).
因此,这不是精确的问题,任何数量的有限位都不能完全符合IEEE754格式的数字。
使用这些位来计算实际数字(最接近的近似值),符号为正。 指数为128 + 4 + 1 = 133 – 127 bias = 6,因此乘数为2 6或64。
尾数由1(隐式基数)加上(对于所有这些位,每个值为1 /(2 n ),因为n从1开始并向右增加), {1/2, 1/16, 1/64, 1/1024, 1/2048, 1/16384, 1/32768, 1/262144, 1/524288, 1/4194304, 1/8388608}
。
当你添加所有这些,你得到1.57968747615814208984375
。
当你乘以先前计算的乘数64
,得到101.09999847412109375
。
所有数字都是用bc
计算的,使用100位十进制数字的刻度,产生大量的尾随零,因此数字应该非常准确。 双重如此,因为我检查了结果:
#include int main (void) { float f = 101.1f; printf ("%.50f\n", f); return 0; }
这也给了我101.09999847412109375000...
您需要阅读有关浮点数如何工作的更多信息,尤其是可表示数字的部分。
你没有给出很多解释,为什么你认为“32位应该足够101.1”,所以很难反驳。
二进制浮点数对于所有十进制数都不适用,因为它们基本上存储数字,等待它,基数为2.如二进制数。
这是一个众所周知的事实,这也是为什么不应该以浮点方式处理金钱的原因。
基数10
数字101.1
是基数2
1100101.0(0011)
。 0011
部分正在重复。 因此,无论您拥有多少位数,该数字都无法在计算机中准确表示。
查看浮点数的IEE754标准,您可以找出为什么double
版本似乎完全显示它。
PS:基数10
中101.1
推导在基数10
为1100101.0(0011)
:
101 = 64 + 32 + 4 + 1 101 -> 1100101 .1 * 2 = .2 -> 0 .2 * 2 = .4 -> 0 .4 * 2 = .8 -> 0 .8 * 2 = 1.6 -> 1 .6 * 2 = 1.2 -> 1 .2 * 2 = .4 -> 0 .4 * 2 = .8 -> 0 .8 * 2 = 1.6 -> 1 .6 * 2 = 1.2 -> 1 .2 * 2 = .4 -> 0 .4 * 2 = .8 -> 0 .8 * 2 = 1.6 -> 1 .6 * 2 = 1.2 -> 1 .2 * 2 = .4 -> 0 .4 * 2 = .8 -> 0 .8 * 2 = 1.6 -> 1 .6 * 2 = 1.2 -> 1 .2 * 2 = .4 -> 0 .4 * 2 = .8 -> 0 .8 * 2 = 1.6 -> 1 .6 * 2 = 1.2 -> 1 .2 * 2....
PPS:如果您想要在基数10
准确存储1/3
的结果,则相同。
你在这里看到的是两个因素的组合:
- IEEE754浮点表示不能准确地表示一整类有理数和所有无理数
-
printf
中舍入(默认为小数点后6位)的效果。 也就是说,使用double
时的错误发生在第6个DP的右侧。
如果你有double
打印的数字,你会发现即使是double
也无法准确表示:
printf ("b: %.16f\n", b); b: 101.0999999999999943
事情是float
和double
使用二进制格式,并非所有浮动指针数字都可以用二进制格式精确表示。
不幸的是,大多数十进制浮点数不能在(机器)浮点中准确表示。 这就是事情的运作方式。
例如,二进制数101.1将表示为1100101.0(0011)
( 0011
部分将永远重复),因此无论您需要存储多少字节,它都将永远不会变得准确。 这是一篇关于浮点二进制表示的小文章, 在这里你可以找到一些将浮点数转换为二进制的例子。
如果你想了解更多关于这个主题的内容,我可以推荐你这篇文章 ,虽然它很长,也不太容易阅读。