Why does std::round offer versions returning long and long long while std::floor, std::ceil, std::trunc don't?

Viewed 263

I was looking at these related families of standard functions in cppreference: std::round, std::floor, std::ceil and std::trunc.

Is there any reason why std::round is the only one providing specific signatures for long and long long as return type? I am struggling to think of any reason other than historical, but std::round has been added fairly recently with C++11.

1 Answers

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 )

Related