J
Jef Driesen
I need to implement a function to implement the rounding of floating point
values. At the moment i have two different implementations, depending on the
type of the return value (integer or double).
// Integer calculation (fast)
int iround(const double x) {
return (x>=0 ? static_cast<int>(x + 0.5) : static_cast<int>(x + 0.5)
}
// Floating point calculation (slow)
double dround(const double x) {
return (x>=0 ? floor(x + 0.5) : ceil(x + 0.5)
}
But then I have 2 different functions for the same functionality. To solve
this, I created a template function with 2 specialisations:
template <typename T> T round(const double x);
template <>
int round(const double x) {
return iround(x);
}
template <>
double round<double>(const double x) {
return dround(x);
}
I can add specialisations for the other data types (float, long, short,
char, signed/unsigned,...) as well. I did not add a 'default'
specialisation, because the round function is only usefull for numeric
types. At this time my function accepts only double precision floating point
values, and I would like to have equivalent functions for float, double and
long double. I did add a second template parameter S for the source type:
template <typename T, typename S>
T round(const S x) {
...
}
The problem is now how do I implement this function?
I could add a specialisation for every combination of S and T, but this is a
lot of work, and there are only 4 usefull implementations possible:
- S and T are floating point type -> implementation using floor/ceil
(dround)
- S is a floating point type and T an integer type -> implementation using
static_cast (iround)
- S is an integer type and T is an integer or floating point type -> simply
static_cast to T
- otherwise no implementation is necessary (should not compile if this is
possible, to prevent e.g. round<double>(std::complex) )
I was thinking of using only one 'default' specialisation and using
std::numeric_limits:
template <typename T, typename S>
T round(const S x)
{
if (std::numeric_limits<S>::is_integer)
// Integer type needs no rounding.
return static_cast<T>(x);
// Find rounding error.
const S round_error = std::numeric_limits<S>::round_error();
if (std::numeric_limits<T>::is_integer) {
// Integer calculation (fastest).
return (x>=0 ? static_cast<T>(x + round_error) : static_cast<T>(x +
round_error)
} else {
// Floating point calculation (slower).
return (x>=0 ? floor(x + round_error) : ceil(x + round_error)
}
}
But this will results in a performance degradation if the compiler is unable
to optimize (eliminate) the 'if' statements. And the function has also an
implementation for data types other then integer and floating point types.
Any advice on this problem?
values. At the moment i have two different implementations, depending on the
type of the return value (integer or double).
// Integer calculation (fast)
int iround(const double x) {
return (x>=0 ? static_cast<int>(x + 0.5) : static_cast<int>(x + 0.5)
}
// Floating point calculation (slow)
double dround(const double x) {
return (x>=0 ? floor(x + 0.5) : ceil(x + 0.5)
}
But then I have 2 different functions for the same functionality. To solve
this, I created a template function with 2 specialisations:
template <typename T> T round(const double x);
template <>
int round(const double x) {
return iround(x);
}
template <>
double round<double>(const double x) {
return dround(x);
}
I can add specialisations for the other data types (float, long, short,
char, signed/unsigned,...) as well. I did not add a 'default'
specialisation, because the round function is only usefull for numeric
types. At this time my function accepts only double precision floating point
values, and I would like to have equivalent functions for float, double and
long double. I did add a second template parameter S for the source type:
template <typename T, typename S>
T round(const S x) {
...
}
The problem is now how do I implement this function?
I could add a specialisation for every combination of S and T, but this is a
lot of work, and there are only 4 usefull implementations possible:
- S and T are floating point type -> implementation using floor/ceil
(dround)
- S is a floating point type and T an integer type -> implementation using
static_cast (iround)
- S is an integer type and T is an integer or floating point type -> simply
static_cast to T
- otherwise no implementation is necessary (should not compile if this is
possible, to prevent e.g. round<double>(std::complex) )
I was thinking of using only one 'default' specialisation and using
std::numeric_limits:
template <typename T, typename S>
T round(const S x)
{
if (std::numeric_limits<S>::is_integer)
// Integer type needs no rounding.
return static_cast<T>(x);
// Find rounding error.
const S round_error = std::numeric_limits<S>::round_error();
if (std::numeric_limits<T>::is_integer) {
// Integer calculation (fastest).
return (x>=0 ? static_cast<T>(x + round_error) : static_cast<T>(x +
round_error)
} else {
// Floating point calculation (slower).
return (x>=0 ? floor(x + round_error) : ceil(x + round_error)
}
}
But this will results in a performance degradation if the compiler is unable
to optimize (eliminate) the 'if' statements. And the function has also an
implementation for data types other then integer and floating point types.
Any advice on this problem?