C浮点精度
C floating point precision
可能重复:
浮点比较
我对C/C++中浮点运算的准确性有个问题。当我执行以下程序时:
#include <stdio.h>
int main (void) {
float a = 101.1;
double b = 101.1;
printf ("a: %fn", a);
printf ("b: %lfn", b);
return 0;
}
结果:
a: 101.099998
b: 101.100000
我认为float应该有32位,所以应该足以存储101.1为什么?
您只能在IEEE754中准确地表示数字(至少对于单精度和双精度二进制格式),前提是它们可以通过将两个(即2-n
,如1
、1/2
、1/4
、1/65536
等)的倒幂加在一起来构造,并受可用于精度的位数的限制。
在浮点(23位精度)或双精度(52位精度)提供的缩放范围内,没有两个倒幂的组合可以使您精确地达到101.1。
如果你想要一个关于两个东西的倒幂是如何工作的快速教程,请看这个答案。
将该答案中的知识应用于您的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,因此乘数为26或64。
尾数由1(隐式基数)加上(对于所有这些比特,每个比特值为1/(2n),因为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 <stdio.h>
int main (void) {
float f = 101.1f;
printf ("%.50fn", f);
return 0;
}
其也给予我CCD_ 13。
您需要阅读更多关于浮点数如何工作的信息,尤其是关于可表示数字的部分。
对于为什么你认为"32位应该足够101.1",你没有给出太多解释,所以很难反驳。
二进制浮点数并不能很好地适用于所有的十进制数字,因为它们基本上将数字存储在以2为基数的二进制浮点数中。如二进制。
这是一个众所周知的事实,也是为什么永远不应该以浮点形式处理金钱的原因。
基数10
中的编号101.1
是基数2
中的1100101.0(0011)
。0011
部分正在重复。因此,无论你有多少个数字,这个数字都无法在计算机中准确表示。
查看IEE754浮点标准,您可以发现为什么double
版本似乎完全显示了这一点。
PS:101.1
在10
基础上的推导为2
基础上的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:如果你想把1/3
的结果准确地存储在基础10
中,那也是一样的。
如果您有更多的数字要打印double
,您将看到即使是double
也无法准确表示:
printf ("b: %.16fn", b);
b: 101.0999999999999943
问题是float
和double
使用二进制格式,并不是所有的浮动指针数都能用二进制格式准确表示。
您在这里看到的是两个因素的组合:
- IEEE754浮点表示不能准确地表示一整类有理数和所有无理数
printf
中四舍五入(此处默认为小数点后6位)的效果。也就是说,当使用double
时的错误发生在第六个DP的右边
不幸的是,大多数十进制浮点数无法用(机器)浮点精确表示。事情就是这样运作的。
例如,二进制中的数字101.1将像1100101.0(0011)
一样表示(0011
部分将永远重复),因此无论您必须存储多少字节,它都永远不会变得准确。这里有一篇关于浮点的二进制表示的小文章,在这里你可以找到一些将浮点数转换为二进制的例子。
如果你想了解更多关于这个主题的知识,我可以推荐你这篇文章,尽管它很长,不太容易阅读。
- 从"int*"强制转换为"unsigned int"会丢失精度错误
- 如何防止 c++ 在从浮点型转换为双精度型(不适用于 IO)时添加额外的小数?
- 正在将csv文件读取为双精度矢量
- 如何理解将半精度指针转换为无符号长指针和相关的内存对齐
- 我可以信任表示整数的浮点或双精度来保持精度吗
- 如何在C++中的同一函数中使用字符串和双精度
- 特征::矩阵<双精度,1,3> 结构类型函数中的返回类型函数
- 当使用比格式支持的精度更高的精度来显示数字时,会写出什么数据
- 如何计算具有指定类型的表达式的相对精度和绝对精度
- 如何打印boost多精度128位无符号整数
- 检查是否以特定精度给出双精度
- 转换函数,将 std::数组的双精度作为参数或双精度作为参数单独转换
- C 字符串返回字符串的整数/双精度/长整型值
- 为什么将双精度转换为 int 似乎在第 16 位数字之后将其四舍五入?
- 如何使双精度值的 C++ 和 C# 中的结果相同
- 使用浮点数和双精度数的非常小数字的数学
- 使用 Xcode 将双精度存储在数组C++中
- 为什么输出精度没有正确舍入?
- 在 C++ 中将双精度变量写入二进制文件
- 如何从字符串转换为双精度*