为啥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);
 

您很快就会发现有 数千 亿个整数无法表示为 floats。例如,16,777,219 和 16,777,221 之间的所有整数都表示为 16,777,220。

再次编辑运行上面的程序表明有 2,071,986,175 整数不能精确地表示为floats。剩下的大约只有 1 亿个正整数可以正确放入 float。这意味着将 21 中的一个整数放入浮点数时,它是正确的。

我希望负整数的数字相同。

【讨论】:

您的代码将浮点数(较大)转换为整数(较小),因此我支持可能存在数据丢失。我的问题是关于 int 浮动 @Dr Deo:i 是一个整数,然后我将其转换为 float。如果精度没有丢失,那么将其转换回整数将返回相同的数字。事实并非如此。 @Stephen:您已经更正了 23->24,但仍然不是每个。每个正偶数 float 中精确表示。 “我们可以赚到几千,为什么还要赚上千……十亿 @Robert Fraser:是的,我严重地低估了你不能放入float的整数数量。【参考方案2】:

在大多数架构上,intfloat 的大小相同,因为它们具有相同的位数。但是,在浮点数中,这些位在指数和尾数之间分开,这意味着浮点数中的精度实际上比 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语言,自动类型转换怎么做?

C语言双目运算符两边的运算数类型不一致系统自动转换的规则是啥?比如1.0/2=0.5那为啥不是1.0/2=0呢?

java float double精度为啥会丢失

关于数据丢失的警告 c++/c

c语言为啥警告说从“int”转换到“float”,可能丢失数据

c语言 不同数据类型间的混合运算