对于二进制浮点数系统中的数字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} } 浮点数格式(类型)的指数范围。
答案 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::nextafter
和std::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可能会更多地参与,但你会需要它?