I would like to remove last 2 or 3 significant digits for floating number in C++ in efficient way. To formulate the question more accurately -- I would like to discard last few mantissa bits in floating number representation.
Some background: I can arrive to the same floating point number using different ways. For example, if I use bilinear interpolation over rectangle with equal values in its corners results will vary in last couple of digits in different points of the rectangle due to machine accuracy limits. The absolute order of these deviations depends on order of interpolated values. For example if p[i]~1e10 (i == 1..4, values at corners of rectangle) then interpolation error caused by machine accuracy is ~1e-4 (for 8-byte floats). If p[i]~1e-10 then error would be ~1e-24. As the interpolated values are used to calculate 1st or 2nd order derivatives I need to 'smooth out' these difference. One idea is to remove last couple of digits off final result. Below is my take on it:
#include <iostream> // std::cout
#include <limits> // std::numeric_limits
#include <math.h> // fabs
template<typename real>
real remove_digits(const real& value, const int num_digits)
{
//return value;
if (value == 0.) return 0;
const real corrector_power =
(std::numeric_limits<real>::digits10 - num_digits)
- round(log10(fabs(value)));
//the value is too close to the limits of the double minimum value
//to be corrected -- return 0 instead.
if (corrector_power > std::numeric_limits<real>::max_exponent10 -
num_digits - 1)
{
return 0.;
}
const real corrector = pow(10, corrector_power);
const real result =
(value > 0) ? floor(value * corrector + 0.5) / corrector :
ceil(value * corrector - 0.5) / corrector;
return result;
}//remove_digits
int main() {
// g++ (Debian 4.7.2-5) 4.7.2 --- Debian GNU/Linux 7.8 (wheezy) 64-bit
std::cout << remove_digits<float>(12345.1234, 1) << std::endl; // 12345.1
std::cout << remove_digits<float>(12345.1234, 2) << std::endl; // 12345
std::cout << remove_digits<float>(12345.1234, 3) << std::endl; // 12350
std::cout << std::numeric_limits<float>::digits10 << std::endl; // 6
}
It works, but uses 2 expensive operations -- log10 and pow. Is there some smarter way of doing this? As follows from above to achieve my goals I do not need to remove actual decimal digits, but just set 3-4 bits in mantissa representation of floating number to 0.