0

我想计算机器 Epsilon,这是使用 C++ 的不同数据类型e给出的最小可能数:和.1 + e > 1floatdoublelong double

这是我的代码:

#include <cstdio>

template<typename T>
T machineeps() {

  T epsilon = 1;
  T expression;

  do {
    epsilon = epsilon / 2;
    expression = 1 + epsilon;
  } while(expression > 1);

  return epsilon;
}

int main() {
  auto epsf = machineeps<float>();
  auto epsd = machineeps<double>();
  auto epsld = machineeps<long double>();

  std::printf("epsilon float: %22.17e\nepsilon double: %22.17e\nepsilon long double: %Le\n", epsf, epsd, epsld);

  return 0;
}

但我得到了这个奇怪的输出:

epsilon float: 5.96046447753906250e-008
epsilon double: 1.11022302462515650e-016
epsilon long double: -0.000000e+000

float和的值double是我所期望的,但是,我无法解释这种long double行为。

有人可以告诉我出了什么问题吗?

4

2 回答 2

2

我无法重现您的结果。我得到:

epsilon 长双倍:5.421011e-20

无论如何,从逻辑上讲,代码应该是这样的:

template<typename T>
T machineeps() {
  T epsilon = 1, prev;
  T expression;

  do {

    prev = epsilon;
    epsilon = epsilon / 2;
    expression = 1 + epsilon;

  } while (expression > 1);

  return prev;  // <-- `1+prev` yields a result different from one
}

在我的平台上,它产生的值类似于std::numeric_limits::epsilon

epsilon 浮点数:1.19209289550781250e-07

epsilon 双倍:2.22044604925031308e-16

epsilon 长双倍:1.084202e-19

(注意不同的数量级)

于 2017-11-06T09:12:49.650 回答
0

这里发生了几件事。

首先,浮点数学通常以最大可用精度完成,而不管浮点变量的实际声明类型如何。因此,例如,对floats 的算术通常在 Intel 硬件上以 80 位精度完成(Java 最初禁止这样做,要求所有浮点数学都以类型的精确精度完成;这会扼杀浮点性能,并且他们很快就放弃了这条规则)。存储浮点计算的结果应该将值截断为适当的类型,但默认情况下大多数编译器会忽略这一点。你可以告诉你的编译器不允许这样做;开关取决于编译器。照原样,您不能依赖此处计算的结果。

其次,代码中的循环在 的值不大于 1 时终止1 + epsilon因此返回值将小于epsilon 的真实值。

第三,再加上第二个,一些浮点实现不做次正规值;一旦指数变得小于可以表示的最小值,该值为 0。这可能就是您在此处看到的long double值。IEEE 浮点对零的处理不那么突然——一旦达到最小指数,较小的值就会逐渐失去精度。在最小归一化值和 0 之间有相当多的值。

于 2017-11-06T14:13:27.697 回答