C ++中的最后一个计算单元是否有标准功能?

时间:2019-01-31 12:34:51

标签: c++ floating-point

对于二进制浮点数系统中的数字x,最后一位的单位定义为ulp(x) = 2^{E(x)} * 2^{1 - p} = 2^{E(x)}*epsilon,其中指数是用来表示{的基数2的指数。 {1}} x是数字格式的精度(p是双精度格式),53是机器公差。

看着epsilon = 2^{1-p},我希望找到类似std::numeric_limits的东西,但这似乎不可用。或类似std::numeric_limits<double>::ulp(x)的东西,我可以乘以std::numeric_limits<double>::exponent(x),但它不存在。 std::numeric_limits<double>::epsilon()函数似乎在boost中可用。

I can code something myself,但如果有的话,我宁愿使用标准化代码。我错过了文档中的内容吗?

编辑:为了更清楚一点,我在说的是浮点数ulp的指数计算,而不是x或{{1} } 浮点数格式(类型)的指数范围。

3 个答案:

答案 0 :(得分:1)

标准库确实没有string.replace(new RegExp('\\$(unique-result)', 'g'), changeString) 函数。

  

2 ^ {E(x)} * epsilon

可以通过以下方式计算:

ulp

注意:仅适用于正常浮点值。对于非正常值,甚至可能不存在可以表示ULP的浮点值。


  

我希望找到类似int exp; std::frexp(std::fabs(x), &exp); double result = std::ldexp(std::numeric_limits<double>::epsilon(), exp - 1); 的东西,但这似乎不可用。或类似std::numeric_limits<double>::ulp(x)

std::numeric_limits<double>::exponent(x)仅包含常量(或返回常量的函数)。它没有执行计算的任何功能,因此也没有接受输入值的功能。 numeric_limits标头具有计算功能。


一般来说,请读者注意:如果您要查找下一个/上一个可表示的值,请使用<cmath>函数系列,而不是添加/减去ULP。

答案 1 :(得分:0)

我找到了一个替代方法,它不适合注释,因此我发布了答案。

我认为也可以通过使用std::nextafterstd::nexttoward来完成:

template<typename FT> 
std::enable_if_t<std::is_floating_point_v<FT>, FT> 
ulp(FT x)
{
    if (x > 0)
        return std::nexttoward(x, std::numeric_limits<FT>::infinity()) - x;
    else 
        return x - std::nexttoward(x, -std::numeric_limits<FT>::infinity());
}

如果nexttoward产生一个浮点数,则该值是有效的;如果浮点数是正数,则该浮点数紧跟着INF;如果负数,它的浮点数紧跟着-INF。由于此数字是浮点数,因此与x的距离将为ulp(x),并且没有舍入误差。

这是一个用于此想法的测试程序,它似乎在不使用幂函数和手动计算指数的情况下也可以工作(它可能在幕后进行了类似的操作)。我通过检查进行了测试:

// Check ulp by adding 0.5ulp to x and let the rounding to nearest round to x. 
assert((x + (0.5 *ulpx) == x));

这是它的MWE:

#include <cmath>
#include <cassert>
#include <iostream>
#include <iomanip>

using namespace std;

template<typename FT> 
std::enable_if_t<std::is_floating_point_v<FT>, FT> 
ulp(FT x)
{
    if (x > 0)
        return std::nexttoward(x, numeric_limits<FT>::infinity()) - x;
    else 
        return x - std::nexttoward(x, -numeric_limits<FT>::infinity());
}

void test_ulp(double x)
{
    double ulpx = ulp(x);

    std::cout << "ulpx = " << std::setprecision(25) << ulpx << std::endl;

    double xNext = x + ulpx; 

    // Check ulp by adding 0.5ulp to x and let the rounding to nearest round to x. 
    if (x > 0)
        assert((x + (0.5 *ulpx) == x));
    else 
        assert((x - (0.5 *ulpx) == x));

    std::cout << std::setprecision(25) <<  x << " + " << ulpx 
        << " == " << xNext << std::endl; 
}

int main()
{
    // Denormalized test. 
    test_ulp(0.); 

    // Epsilon test. 
    test_ulp(1.); 

    // Random number test. :) 
    test_ulp(42.); 

    // Epsilon test. 
    test_ulp(-1.); 
}

编辑:试图找到nexttoward的内容,调查了gcc STL source code并仅发现以下内容:

#ifndef __CORRECT_ISO_CPP11_MATH_H_PROTO_FP
   constexpr float
   nexttoward(float __x, long double __y)
   { return __builtin_nexttowardf(__x, __y); }

   constexpr long double
   nexttoward(long double __x, long double __y)
   { return __builtin_nexttowardl(__x, __y); }
 #endif

 #ifndef __CORRECT_ISO_CPP11_MATH_H_PROTO_INT
   template<typename _Tp>
     constexpr typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value, 
                                               double>::__type
     nexttoward(_Tp __x, long double __y)
     { return __builtin_nexttoward(__x, __y); }
 #endif

答案 2 :(得分:0)

从技术上讲,解决方案可能是提取指数位,然后从中减去1074。这使ULP的指数。如果需要带符号的ulp,请按此指数缩放符号。像

int exp = extract_exponent_bits(x):
Int sign = 1 - 2* extract_sign_bit(x);
return ldexp((double)sign,exp-1074);

比特提取在太阳fdlibm留作exercize,指针别名等没有现今的选项,所以只是memcpy到UINT64,移>> 52,等...

泛化到非IEEE可能会更多地参与,但你会需要它?