What I'd prefer is something more like:
constexpr Fraction BLOCK_DOWNLOAD_TIMEOUT_BASE{1};
constexpr Fraction BLOCK_DOWNLOAD_TIMEOUT_PER_PEER{1,2};
so that you can define the constants meaningfully, but still have all the calculations done with integer math. You can make that actually work relatively easily:
class Fraction {
public:
constexpr Fraction(int64_t n, int64_t d) : num{n}, den{d} { }
explicit constexpr Fraction(int64_t n) : num{n}, den{1} { }
int64_t num = 0, den = 1;
};
constexpr Fraction operator+(Fraction l, Fraction r) { return Fraction{l.num*r.den + r.num*l.den, l.den*r.den}; }
constexpr Fraction operator*(Fraction l, int64_t n) { return Fraction{l.num*n, l.den}; }
constexpr std::chrono::microseconds operator*(std::chrono::microseconds t, Fraction f) { return t*f.num/f.den; }
but maintaining a special class for this seems crazy, and C++ stdlib seems to only provide compile-time rationals (via std::ratio) which is useless here, and boost::rational seems to insist on normalising the denominator for no particular reason (and the fixedpoint stuff seems even more overkill). Still not convinced having floating point math is a win compared to just having 1M and 500k as constants, but there doesn't seem to be any better alternatives than those.