【问题标题】:Questions about float characteristics [duplicate]关于浮动特性的问题[重复]
【发布时间】:2017-10-24 21:00:40
【问题描述】:

Q1:为什么不建议像 V1 中那样比较 ==!= 的浮点数?
Q2:V2 中的fabs() 是否像我在V3 中编写的那样工作?
Q3:(x >= y)(x <= y)可以用吗?
Q4:根据维基百科float 的精度在 6 到 9 位之间,在我的例子中是 7 位。那么它取决于什么,我的float 具有 6 到 9 位之间的精度?请参阅 [1]


[1] 浮动特征

来源:Wikipedia 类型 |尺寸 |精度 |范围 浮动 | 4Byte ^= 32Bits | 6-9 位小数 | (2-2^23)*2^127 来源:tutorialspoint 类型 |尺寸 |精度 |范围 浮动 | 4Byte ^= 32Bits | 6 位小数 | 1.2E-38 至 3.4E+38 来源:chortle 类型 |尺寸 |精度 |范围 浮动 | 4Byte ^= 32Bits | 7 位小数 | -3.4E+38 至 +3.4E+38

以下三个代码产生相同的结果,仍然不建议使用第一个变体。

1.变体

#include <stdio.h>    // printf() scanf()
int main()
{
  float a = 3.1415926;
  float b = 3.1415930;

  if (a == b)
  {
    printf("a(%+.7f) == b(%+.7f)\n", a, b);
  }
  if (a != b)
  {
    printf("a(%+.7f) != b(%+.7f)\n", a, b);
  }
  return 0;
}

V1-输出:

a(+3.1415925) != b(+3.1415930)

2。变体

#include <stdio.h>    // printf() scanf()
#include <float.h>    // FLT_EPSILON == 0.0000001
#include <math.h>     // fabs()
int main()
{
  float x = 3.1415926;
  float y = 3.1415930;

  if (fabs(x - y) < FLT_EPSILON)
  {
    printf("x(%+.7f) == y(%+.7f)\n", x, y);
  }
  if (fabs(x - y) > FLT_EPSILON)
  {
    printf("x(%+.7f) != y(%+.7f)\n", x, y);
  }
  return 0;
}

V2-输出:

x(+3.1415925) != y(+3.1415930)

3.变体:

#include <stdio.h>    // printf() scanf()
#include <float.h>    // FLT_EPSILON == 0.0000001
#include <stdlib.h>   // abs()
int main()
{
  float x = 3.1415926;
  float y = 3.1415930;

  const int FPF = 10000000;   // Float_Precission_Factor
  if ((float)(abs((x - y) * FPF)) / FPF < FLT_EPSILON)   // if (x == y)
  {
    printf("x(%+.7f) == y(%+.7f)\n", x, y);
  }
  if ((float)(abs((x - y) * FPF)) / FPF > FLT_EPSILON)   // if (x != y)
  {
    printf("x(%+.7f) != y(%+.7f)\n", x, y);
  }
  return 0;
}

V3-输出:

x(+3.1415925) != y(+3.1415930)

感谢任何帮助、链接、参考和提示!

【问题讨论】:

  • 试试这个:float f; for(f = 0.0; f != 1.0; f += 0.1) printf("%.1f\n");
  • 当您的数字不相等时,您通常不会遇到问题。当您有两个认为相等但==!= 说它们不相等的浮点数时,就会出现问题。
  • @SteveSummit 谢谢你的例子,现在我看到了问题,它忽略了停止条件。

标签: c compare precision


【解决方案1】:

在处理浮点运算时,几乎每一步都可能引入小的舍入误差。将源代码中的十进制数字转换为浮点格式?有一个小错误,除非数字是完全可表示的。添加两个数字?它们的精确总和通常比浮点格式的位数多,因此必须四舍五入以适合。乘法和除法也是如此。取平方根?结果通常是不合理的,不能用浮点格式表示,所以要四舍五入。调用库以获取余弦或对数?确切的结果通常是不合理的,所以它是四舍五入的。而且大多数数学库也有一些额外的错误,因为非常精确地计算这些函数是很困难的。

所以,假设您计算了一些值并在x 中得到结果。它包含了各种错误。然后您计算另一个值并在y 中得到结果。假设,如果用精确的数学计算,这两个值将相等。 xy 中的错误完全相同的可能性有多大?

这不太可能。如果xy 以不同的方式计算,它们会经历不同的误差,它们是否具有相同的总误差本质上是偶然的。因此,即使精确的数学结果相等,x == y 也可能因为错误而为假。

同样,两个精确的数学值可能不同,但错误可能重合,因此 x == y 返回 true。

因此x == yx != y 通常不能用于判断所需的精确数学值是否相等。

有什么用?不幸的是,没有通用的解决方案。您的示例使用 FLT_EPSILON 作为错误阈值,但这没有用。在进行多次浮点运算后,误差很容易累积到超过FLT_EPSILON,无论是绝对误差还是相对误差。

为了进行比较,您需要了解累积误差可能有多大,这在很大程度上取决于您执行的特定计算。您还需要知道误报和误报的后果是什么——是避免错误地陈述两件事相等还是避免错误地陈述两件事不相等更重要?这些问题特定于每种算法及其数据。

【讨论】:

  • 也就是说处理舍入误差的方式总是视情况而定,没有通用的方法?
  • 是的,差不多。浮点数被广泛用于计算近似值。将其用于精确或非常精确的工作需要工程上的努力。
  • 不错的答案。作为你所说的一个非常简单的例子,我尝试了double x = 37; double y = pow(sqrt(x), 2); if(x == y) printf("equal\n"); else printf("unequal\n");,果然,它打印了unequal
【解决方案2】:

因为在 64 位机器上你会发现 0.1*3 = 0.30000000000000004 :-)

请参阅作为 cmets 提供的链接 @yano 和 @PM-77-1。

【讨论】:

    【解决方案3】:

    您知道机器使用 0 和 1 存储所有内容。 还要知道,并非每个浮点值都可以在有限位内以二进制表示。 计算机存储给定数字的可能最近的可表示二进制。

    所以它们在计算机眼中是 2.0000001 和 2.0000000 之间的差异(但我们说它们是相等的!)。

    这种麻烦并不总是出现,但有风险。

    【讨论】:

    • 不是浮点数的存储方式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-25
    • 1970-01-01
    • 2020-03-08
    • 1970-01-01
    • 2023-04-08
    • 2011-09-22
    相关资源
    最近更新 更多