为啥C语言中用float类型进行较大值的运算会丢失较多精度
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为啥C语言中用float类型进行较大值的运算会丢失较多精度相关的知识,希望对你有一定的参考价值。
如题
单精度浮点型(Float)可以用来实数,占用4个字节32位存储空间,数值范围较大,可以表示-3.4E38到+3.4E38,其中阶码7位,符号位1位,尾数24位。但在运行较大数值运算的时候,将有可能产生溢出,得到错误的结果;也有可能有效数字位数超过7位的时候,将会四舍五入,会丢失较多精度。考虑到上述情况,可以定义double float(双精度浮点型),采用8字节64位存储空间,可以表示更大的数字 参考技术A 这个时候应该用double,float只能保留六个有效数字。
关于数据丢失的警告 c++/c
【中文标题】关于数据丢失的警告 c++/c【英文标题】:Warning about data loss c++/c 【发布时间】:2010-04-17 14:39:53 【问题描述】:我收到关于可能丢失数据的良性警告
警告 C4244:“argument”:从“const int”转换为“float”,可能会丢失数据
问题
我记得好像 float 的精度比 int 大。那么如果我从较小的数据类型 (int) 转换为较大的数据类型 (float) 怎么会丢失数据呢?
【问题讨论】:
与您的具体问题无关,但如果您认为浮点数可以比双精度数更快,那您就错了 - 浮点数的目的是最大限度地减少存储需求,这对于现代人来说很少是问题应用程序。您默认选择的数据类型应该是 double,而不是 float。 @Neil:这在很大程度上取决于 CPU。在许多架构中,float 的速度明显快于 double。 @jalf 可能,但在 C 或 C++ 代码中的大多数情况下,浮点数无论如何都会提升为双精度数。 @jalf:x86 不是其中之一。使用 Visual C++ 查看 OP? 【参考方案1】:因为float
的数字不准确。即使float
的最大值要高得多,您也无法将int
可以包含的所有可能值都表示为float
。
例如,运行这个简单的程序:
#include <stdio.h>
int main()
for(int i = 0; i < 2147483647; i++)
float value = i;
int ivalue = value;
if(i != ivalue)
printf("Integer %d is represented as %d in a float\n", i, ivalue);
您很快就会发现有 数千 亿个整数无法表示为 float
s。例如,16,777,219 和 16,777,221 之间的所有整数都表示为 16,777,220。
再次编辑运行上面的程序表明有 2,071,986,175 正整数不能精确地表示为float
s。剩下的大约只有 1 亿个正整数可以正确放入 float
。这意味着将 21 中的一个整数放入浮点数时,它是正确的。
我希望负整数的数字相同。
【讨论】:
您的代码将浮点数(较大)转换为整数(较小),因此我支持可能存在数据丢失。我的问题是关于 int 浮动 @Dr Deo:i
是一个整数,然后我将其转换为 float
。如果精度没有丢失,那么将其转换回整数将返回相同的数字。事实并非如此。
@Stephen:您已经更正了 23->24,但仍然不是每个。每个正偶数 float 中精确表示。
“我们可以赚到几千,为什么还要赚上千……十亿”
@Robert Fraser:是的,我严重地低估了你不能放入float
的整数数量。【参考方案2】:
在大多数架构上,int
和 float
的大小相同,因为它们具有相同的位数。但是,在浮点数中,这些位在指数和尾数之间分开,这意味着浮点数中的精度实际上比 int 少。不过,这可能只是较大整数的问题。
在 int
是 32 位的系统上,double
通常是 64 位,因此可以精确地表示任何 int。
【讨论】:
【参考方案3】:这两种类型都由 4 个字节(32 位)组成。 其中只有一个允许分数(浮点数)。
以此为例;
34.156
(整数).(分数)
现在使用你的逻辑; 如果其中一个必须保存分数信息(毕竟它应该代表一个数字),那么这意味着它的整数部分的位数更少。
因此,浮点数可以表示小于 int 类型能力的最大整数。
更具体地说,“int”使用 32 位来表示整数(最大无符号整数为 4,294,967,296)。 “浮点数”使用 23 位来执行此操作(最大无符号整数 8,388,608)。
这就是为什么当您从 int 转换为 float 时可能会丢失数据。
示例: 整数 = 1,158,354,125
您不能将此数字存储在“浮点数”中。
更多信息请访问:
http://en.wikipedia.org/wiki/Single_precision_floating-point_format
http://en.wikipedia.org/wiki/Integer_%28computer_science%29
【讨论】:
这有点不对,有几个原因。其中之一是float
尾数为 24 位,而不是 23 位。因此,直到 16777216 的每个无符号整数都可以精确表示。
IEEE 754 单浮点数有 23 位尾数,由于指数已标准化,因此有效范围为 24 位。尾数从指数中“偷了一点”。【参考方案4】:
精度无关紧要。 int 的精度为 1,而典型浮点数(IEEE 754 单精度)的精度约为 5.96e-8。重要的是这两种格式可以表示的数字集。如果存在 int 可以准确表示而 float 不能表示的数字,则可能会丢失数据。
如今,浮点数和整数通常都是 32 位,但这并不能保证。假设在您的机器上是这种情况,那么必然存在 float 无法准确表示的 int 值,因为显然存在 int 无法准确表示的 float 值。如果两种格式有效地使用相同的位数,则一种格式的范围不能是另一种格式的正确超集。
一个 32 位的 int 实际上有 31 位编码数字的绝对值。一个 IEEE 754 浮点数实际上只有 24 位用于编码尾数(一个隐式)。
【讨论】:
【参考方案5】:事实上,float 和 int 都使用 32 位表示。整数值使用所有 32 位,因此它可以容纳从 -231 到 231-1 的数字。但是,浮点数使用 1 位作为符号(包括 -0.0f)和 8 位作为指数。意味着尾数剩下 32 - 9 = 23 位。但是,浮点数假设如果尾数和指数不为零,则尾数以 1 开头。因此,您的整数或多或少有 24 位,而不是 32 位。但是,因为它可以移动,它可以容纳更多超过 224 个整数。
A floating point uses a Sign, an eXponent, and a Mantissa
S X X X X X X X X M M M M M M M M M M M M M M M M M M M M M M M
An integer has a Sign, and a Mantissa
S M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M
所以,一个 29 位整数,例如:
0 0 0 1 1 1 1 1 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
适合浮动,因为它可以移动:
0 0 0 1 1 1 1 1 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
| | |
| +-----------+ +-----------+
| | |
v v v
S X X X X X X X X M M M M M M M M M M M M M M M M M M M M M M M
0 1 0 0 1 1 0 1 1 1 1 1 1 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0
指数表示有偏差的移位(尾数的移位减去 128,如果我是正确的,移位从小数点开始计算)。这清楚地向您表明,如果您必须移动 5 位,您将丢失低 5 位。
因此,这个其他整数可以转换为丢失 2 位的浮点数(即,当您转换回整数时,最后两位 (11) 设置为零 (00),因为它们没有保存在浮动):
1 1 1 0 0 1 1 1 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1
| ||
| || complement
| vv
| 0 0 1 1 0 0 0 0 1 1 1 1 0 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 1
| | | | | | | |
| +-----------+ +-----------+ +-+-+-+-+--> lost bits
| | |
v v v
S X X X X X X X X M M M M M M M M M M M M M M M M M M M M M M M
1 1 0 0 1 1 0 1 1 1 0 0 0 0 1 1 1 1 0 0 1 1 1 1 0 1 1 1 1 1 1 1
注意:对于负数,我们首先生成补码,即减1,然后将所有位从0反转为1。即补码 em> 是保存在尾数中的内容。但是,该标志仍会按原样复制。
真的很简单。
重要提示:是的,整数中的第一个 1 是符号,然后尾数中不复制下一个 1,假定为 1,因此不需要。 p>
【讨论】:
【参考方案6】:float
通常采用标准 IEEE 单精度格式。这意味着float
中只有 24 位精度,而int
可能是 32 位的。因此,如果您的 int
包含一个绝对值无法容纳 24 位的数字,您可能会将其四舍五入为最接近的可表示数字。
【讨论】:
Nit:“精度”有一个标准含义,只是巧合地与可表示数字集的范围相关。 “精度”是可以加到 1.0 并产生不同于 1.0 的数字的最小正数。因此,int 的精度为 1(一位,而不是 31 或 32)。 IEEE 754 单浮点的精度约为 5.96e-8(24 位)。 @Jive Dadson:我以更随意(非正式)的方式使用了“精确”一词。在这个答案中,它显然意味着“有效位数”。【参考方案7】:我对此类问题的基本回答是阅读此内容 - What Every Computer Scientist Should Know About Floating-Point Arithmetic。
【讨论】:
以上是关于为啥C语言中用float类型进行较大值的运算会丢失较多精度的主要内容,如果未能解决你的问题,请参考以下文章
C语言双目运算符两边的运算数类型不一致系统自动转换的规则是啥?比如1.0/2=0.5那为啥不是1.0/2=0呢?