在C上机器epsilon

pu82cl6c  于 2023-02-21  发布在  其他
关注(0)|答案(2)|浏览(91)

为什么下面的两个printf语句在(float)强制类型转换存在时打印相同的值,而在它被删除时却不打印相同的值?

#include <float.h>
#include <stdio.h>

int main(void)
{
    float eps = 1.0;
    while ((float)(1 + eps/2.0) != 1.0)
        eps /= 2.0;

    printf("%e\n", eps);
    printf("%e\n", FLT_EPSILON);
}
bzzcjhmw

bzzcjhmw1#

在编程语言中,C非常乐于在比您预期的更广泛的类型中进行算术运算。

eps/2.0

在除法之前将eps转换为double,因为数值常量2.0的类型为double。您可以通过编写2.0f来避免这种情况,但即使这样,有一条规则说,只要选择被记录在案,编译器就被允许在double甚至long double中执行 * 所有 * 浮点运算。(C2011第5.2.4.2.2节第9段)
对于FLT_EVAL_METHOD == 0的实现,可以通过编写以下代码来避免强制转换

float eps = 1.0f;
    while (1.0f + eps/2.0f != 1.0f)
        eps /= 2.0f;

但如果FLT_EVAL_METHOD非零,则必须使用强制类型转换。

nr9pn0ug

nr9pn0ug2#

    • 节省时间,启用所有警告**

对于OP的代码,一个功能良好的编译器会报告如下内容

warning: conversion from 'double' to 'float' may change value [-Wfloat-conversion]  
eps /= 2.0;

这就暗示了floatdouble之间发生了一些事情。

    • double或更宽的中间数学**

使用double常量或取决于FLT_EVAL_METHOD,可能会产生各种结果。FLT_EVAL_METHOD == 2表示可以使用long double范围和精度完成中间FP数学运算。
故事的寓意:使用浮点数时要小心。

int main(void) {
  printf("FLT_EVAL_METHOD %d\n", FLT_EVAL_METHOD);
  printf("FLT_EPSILON     %e\n", FLT_EPSILON);
  puts("");

  {
    float eps = 1.0;
    while ((float) (1 + eps / 2.0) != 1.0) // Cast and double math
      eps /= 2.0;
    printf("%e\n", eps);
  }

  {
    float eps = 1.0;
    while ((1 + eps / 2.0) != 1.0)  // No cast, double
      eps /= 2.0;
    printf("%e\n", eps);
  }

  {
    float eps = 1.0;
    while ((1.0f + eps / 2.0f) != 1.0f)  // No cast, float
      eps /= 2.0f;
    printf("%e\n", eps);
  }

  {
    float eps = 1.0;
    for(;;) {
      float oneplus = 1.0f + eps/2.0f; // Coerce float math
      if (oneplus == 1.0f) break;
      eps /= 2.0f;
    }
    printf("%e\n", eps);
  }
}

FLT_EVAL_METHOD == 0输出

FLT_EVAL_METHOD 0
FLT_EPSILON     1.192093e-07

1.192093e-07  // Cast, double math
2.220446e-16  // No cast, double  math
1.192093e-07  // No cast, float math
1.192093e-07  // No cast, Coerced float math

FLT_EVAL_METHOD == 2输出

FLT_EVAL_METHOD 2
FLT_EPSILON     1.192093e-07

1.192093e-07
1.084202e-19  // No cast, long double  math
1.084202e-19  // No cast, long double  math
1.192093e-07
    • 舍入模式**

舍入模式可能会影响事情,但我还没有一个样本来演示它。

相关问题