I'm trying to do integer division + modulo using doubles (for spline-based interpolation), but I've encountered some issues relating to floating-point precision when using std::floor and std::fmod.
I've been using the equivalent of div1 below, but at 50 it produces incorrect results (that is, the integer part is 3, but the modulo part is the divisor minus epsilon). div2 works but is rather convoluted. div3 is at least consistent, but does not return the type of result I would like (the remainder may be negative, so it would need futher manipulation before I could use it).
#include <iostream>
#include <cmath>
std::pair<double, double> div1(int num, double denom){
double whole = std::floor(num / denom);
double remain = std::fmod(num, denom);
return {whole, remain};
}
std::pair<double, double> div2(int num, double denom){
double floatdiv = num / denom;
double whole;
double remain = std::modf(floatdiv, &whole);
return {whole, remain * denom};
}
std::pair<double, double> div3(int num, double denom){
double whole = std::round(num / denom);
double remain = std::remainder(num, denom);
return {whole, remain};
}
int main() {
double denom = 100.0 / 6;
int divtype = 0;
for(auto div: {div1, div2, div3}){
std::cerr << "== Using div" << ++divtype << " for this run ==\n";
for(int i = 40; i <= 60; ++i){
auto res = div(i, denom);
std::cerr << i << ": " << res.first << ", " << res.second << " = " << res.first * denom + res.second << "\n";
}
auto oldprec = std::cerr.precision(64);
auto res = div(50, denom);
std::cerr << 50 << ": " << res.first << ", " << res.second << " = " << res.first << " * " << denom << " + " << res.second << " = " << std::floor(res.first) * denom + res.second << "\n";
std::cerr.precision(oldprec);
std::cerr << "\n";
}
return 0;
}
For the case of 50, the following results are produced:
- div1: 3, 16.6666...
- div2: 3, 0
- div3: 3, -3e-15
Am I doing something wrong, or is std::floor(num / denom) + std::fmod(num, denom) not reliable? If so, what is a good replacement? Is div2 the best option?
Version of code example with most answers included: https://ideone.com/l2wGRj