我试图构建一个模板来比较基于C++https://floating-point-gui.de/errors/comparison/的浮点数或双精度数。 该版本要求将 epsilon 值传递给代码,但我想为系统使用默认的 FLT_EPSILON、FLT_MIN、FLT_MAX、DBL_EPSILON、DBL_MIN和DBL_MAX,并在 float.h 中可用。
有没有办法在语句中使用类型名,例如:
T epsilon;
if ( T == DBL_TYPE ) epsilon = DBL_EPSILON
else epsilon = FLT_EPSILON ????
这将使例程,因此我不必专门化它,从而导致一个定义规则(ODR(问题。
我是模板的新手,所以我不知道如何提问。
template <typename T>
bool IsNearEqual( T test_1, T test_2 )
{
if ( test_1 == test_2 ) return true;
return false;
}
template <>
bool IsNearEqual( float test_1, float test_2 )
{
const float FLT_NORMAL = ( (long)1 << 23 ) * FLT_EPSILON;
if ( test_1 == test_2 )
{ // shortcut, handles infinities
return true;
}
float test_1Abs = dfAbs( test_1 );
float test_2Abs = dfAbs( test_2 );
float test_1_2AbsDif = dfAbs( test_1 - test_2 );
float test_1_2Sum = test_1Abs + test_2Abs;
if ( test_1 == 0 || test_2 == 0 || test_1_2Sum < FLT_NORMAL )
{ // test_1 or test_2 is zero or both are extremely close to it relative error is less meaningful here
return test_1_2AbsDif < ( FLT_EPSILON * FLT_NORMAL );
}
else
{ // use relative error
return test_1_2AbsDif / dfMin( test_1_2Sum, FLT_MIN ) < FLT_EPSILON;
}
return false;
}
template <>
bool IsNearEqual( double test_1, double test_2 )
{
const double DBL_NORMAL = ( (long long)1 << 52 ) * DBL_EPSILON;
if ( test_1 == test_2 )
{ // shortcut, handles infinities
return true;
}
double test_1Abs = dfAbs( test_1 );
double test_2Abs = dfAbs( test_2 );
double test_1_2AbsDif = dfAbs( test_1 - test_2 );
double test_1_2Sum = test_1Abs + test_2Abs;
if ( test_1 == 0 || test_2 == 0 || test_1_2Sum < DBL_NORMAL )
{ // test_1 or test_2 is zero or both are extremely close to it relative error is less meaningful here
return test_1_2AbsDif < ( DBL_EPSILON * DBL_NORMAL );
}
else
{ // use relative error
return test_1_2AbsDif / dfMin( test_1_2Sum, DBL_MAX ) < DBL_EPSILON;
}
return false;
}
template <typename T, typename U>
T dfMin( T test_1, U test_2 )
{
T out;
out = test_1;
if ( out > test_2 ) out = (T)test_2;
return out;
}
template <typename T, typename U>
T dfMax( T test_1, U test_2 )
{
T out;
out = test_1;
if ( test_2 > out ) out = (T)test_2;
return out;
}
template <typename T>
T dfAbs( T test )
{
T out;
out = test;
if ( test < 0.0 ) out = -test;
return out;
}
谢谢 朗克
这是您的代码,其中包含一些 SFINAE 并使用注释中建议的std::numeric_limits
。
template <class T, std::enable_if_t<std::is_floating_point<T>{}>* = nullptr>
bool IsNearEqual(T test_1, T test_2) {
using nl = std::numeric_limits<T>;
const T NORMAL = ((long)1 << nl::digits) * nl::epsilon();
if (test_1 == test_2) { // shortcut, handles infinities
return true;
}
float test_1Abs = dfAbs(test_1);
float test_2Abs = dfAbs(test_2);
float test_1_2AbsDif = dfAbs(test_1 - test_2);
float test_1_2Sum = test_1Abs + test_2Abs;
if (test_1 == 0 || test_2 == 0 ||
test_1_2Sum < NORMAL) { // test_1 or test_2 is zero or both are extremely
// close to it relative error is less meaningful
// here
return test_1_2AbsDif < (nl::epsilon() * NORMAL);
} else { // use relative error
return test_1_2AbsDif / dfMin(test_1_2Sum, nl::max()) < nl::epsilon();
}
return false;
}
我希望这是最终版本。
#include <type_traits>
#include <limits>
template <class T, std::enable_if_t < std::is_floating_point<T>{} > * = nullptr >
bool IsNearEqual( T test_1, T test_2 )
{
using nl = std::numeric_limits<T>;
const T NORMAL = (T)( (long long)1 << nl::digits ) * nl::epsilon();
if ( test_1 == test_2 )
{ // shortcut, handles infinities
return true;
}
T test_1Abs = dfAbs( test_1 );
T test_2Abs = dfAbs( test_2 );
T test_1_2AbsDif = dfAbs( test_1 - test_2 );
T test_1_2Sum = test_1Abs + test_2Abs;
if ( test_1 == 0 || test_2 == 0 || test_1_2Sum < NORMAL )
{ // test_1 or test_2 is zero or both are extremely
// close to it relative error is less meaningful here
return test_1_2AbsDif < ( nl::epsilon() * NORMAL );
}
else
{ // use relative error
return test_1_2AbsDif / dfMin( test_1_2Sum, (nl::max)() ) < nl::epsilon();
}
return false;
}
将 test_1Abs、test_2Abs、test_1_2AbsDif 和test_1_2Sum的浮点数更改为 T。
也(多头(1 到(长长(1 .
仍然不确定 NORMAL 应该使用 nl::d igits还是 nl::d igits - 1 。 ?????????????
感谢您@0x499602D2和@Evg的帮助。
朗克