This is just my guessing but I think the reason might be, that l
and ll
versions of floor
, ceil
and trunc
can be realized using rint and different rounding modes. For example, llfloor()
could be implemented like this:
#include <cfenv>
#include <cmath>
#pragma STDC FENV_ACCESS ON
long long llfloor(double arg) {
auto save_round = std::fegetround();
std::fesetround(FE_DOWNWARD);
auto ret = llrint(arg);
std::fesetround(save_round);
return ret;
}
One nice property of l
/ll
versions is that they raise FE_INVALID exception when the result is outside the range of the result type. Our llfloor()
does that too:
#include <iostream>
#include <limits>
int main() {
double input = std::nextafter(std::numeric_limits<long long>::max(), INFINITY);
std::cout << "input = " << std::fixed << input << "\n";
std::feclearexcept(FE_ALL_EXCEPT);
auto result = llfloor(input);
if (std::fetestexcept(FE_INVALID)) {
std::cout <<"FE_INVALID was raised\n";
}
std::cout << "result = " << result << "\n";
}
and the output is (or see in godbolt):
input = 9223372036854777856.000000
FE_INVALID was raised
result = -9223372036854775808
You may still be asking "Can't llround
be implemented with llrint
?". Turns out it can't. The FE_TONEAREST rounding mode does rounding to even for halfway cases, while round
does away-from-zero.
Also be aware that the compiler support for accessing or modifying the floating point environment might not be fully there yet:
main.cpp:3: warning: ignoring ‘#pragma STDC FENV_ACCESS’ [-Wunknown-pragmas]
3 | #pragma STDC FENV_ACCESS ON
(related question: Adding two floating-point numbers )