如何确定双重的最小显着变化

时间:2013-10-10 09:23:54

标签: floating-point

我有问题确定给定双变量eps的最小值v,以便

v+eps != v

请注意,这不是典型的问题表任务,因为eps取决于任意 号码v

这不应该通过在for循环中寻找此值来完成。有没有快速的方法来做到这一点, 例如通过位移?独立于编译器,优化标志,平台......

感谢您的回答

3 个答案:

答案 0 :(得分:3)

C99功能nextafter正是您所需要的。或者,使用Boost.Math的nextafter。这是定义定义的实现(它依赖于内存中double的内部表示)。

在撰写本文时,为了比较答案中提供的所有方法,请参阅a live demo以了解其他解决方案是如何失败的。


作为参考,如果你想在我们自己的系统上运行它,这是测试代码:

#include <cmath>
#include <cfloat>
#include <limits>
#include <iostream>
using std::cout;
#include <iomanip>
using std::setprecision;

#include <boost/math/special_functions/next.hpp>

double
epsFor( double x )
{
  union
  {
    double d;
    unsigned long long i;
  } tmp;
  tmp.d = x;
  ++ tmp.i;
  return tmp.d - x;
}

void test(double d)
{
  double d1 = std::nextafter(d,DBL_MAX);
  double d2 = d+std::numeric_limits<double>::epsilon() * d;
  double d3 = d+epsFor(d);
  double d4 = boost::math::nextafter(d, DBL_MAX);
  cout << setprecision(40)
       << "For value of d = " << d << '\n'
       << " std::nextafter: " << d1 << '\n'
       << " Boost solution: " << d4 << '\n'
       << " undefined beh.: " << d3 << '\n'
       << " numeric_limits: " << d2 << '\n';
}

int main()
{
  test(0.1);
  test(986546357654.354687);
}

答案 1 :(得分:1)

我会使用类型惩罚:

double
epsFor( double x )
{
    union
    {
        double d;
        unsigned long long i;
    } tmp;
    tmp.d = x;
    ++ tmp.i;
    double results = tmp.d - x;
    return results;
}

(形式上,这是未定义的行为,但在实践中,我没有 知道现代编译器会失败。)

编辑:

请注意,C ++允许中间程度过高 表达式;因为我们关注的是确切的结果,所以 如果使用,最初发布的功能可能会给出错误的结果 它直接在表达式中,而不是分配给它 一个double。我在函数中添加了一个要避免的赋值 这一点,但请注意,许多编译器标准 在这方面,至少在默认情况下是符合的。 (g ++很好 您需要特殊选项的示例 符合行为,至少在打开优化时。 如果您使用的是g ++,则必须指定 如果您想要正确的结果,请-ffloat-store选项。)

答案 2 :(得分:0)

eps = std::numeric_limits<double>::epsilon() * v;