为啥 std::round 提供返回 long 和 long long 的版本,而 std::floor、std::ceil、std::trunc 不提供?

Posted

技术标签:

【中文标题】为啥 std::round 提供返回 long 和 long long 的版本,而 std::floor、std::ceil、std::trunc 不提供?【英文标题】:Why does std::round offer versions returning long and long long while std::floor, std::ceil, std::trunc don't?为什么 std::round 提供返回 long 和 long long 的版本,而 std::floor、std::ceil、std::trunc 不提供? 【发布时间】:2021-12-17 04:53:53 【问题描述】:

我在 cppreference 中查看了这些相关的标准函数系列:std::roundstd::floorstd::ceilstd::trunc

为什么std::round 是唯一一个为longlong long 提供特定签名作为返回类型的人?除了历史原因,我很难想出任何原因,但最近 C++11 中添加了 std::round

【问题讨论】:

This isn't 100% a duplicate, but it answers your question。 (“std::floor的返回值”的第一个谷歌结果) 返回整数没有意义。 std::round 只是出于某种原因。 @asynts 这是一个有趣的阅读,但它没有回答我的问题,我会想象问题中突出显示的相同限制将适用于 std::round,但不知何故标准库提供了特定的它的功能,而不是地板。 很可能是因为 c 有 exact same set of functions,但有趣的是知道为什么 c 没有为 ceil / floor / @ 添加 long / long long 版本987654341@. @ALX23z std::floor 你的意思是std::trunc,对吧? :-) 不同之处在于,使用强制转换可以溢出,即 UB,而使用 std::trunc 则不能。 【参考方案1】:

这只是我的猜测,但我认为原因可能是,floorceiltrunclll 版本可以使用rint 和不同的rounding modes 来实现。例如,llfloor() 可以这样实现:

#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;

l/ll 版本的一个很好的特性是,当结果超出结果类型的范围时,它们会引发 FE_INVALID exception。我们的llfloor() 也是这样做的:

#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";

输出是(或参见godbolt):

input  =  9223372036854777856.000000
FE_INVALID was raised
result = -9223372036854775808

您可能还在问“不能用llrint 实现llround 吗?”。事实证明它不能。 FE_TONEAREST 舍入模式对中途情况执行 rounding to even,而 round 执行远离零的情况。

另外请注意,编译器对访问或修改 floating point environment 的支持可能尚未完全实现:

main.cpp:3: warning: ignoring ‘#pragma STDC FENV_ACCESS’ [-Wunknown-pragmas]
    3 | #pragma STDC FENV_ACCESS ON

(相关问题:Adding two floating-point numbers)

【讨论】:

以上是关于为啥 std::round 提供返回 long 和 long long 的版本,而 std::floor、std::ceil、std::trunc 不提供?的主要内容,如果未能解决你的问题,请参考以下文章

ajax获取java后台的返回值,long的值为啥会四舍五入解决办法

为啥Java中的BitSet使用long数组做内部存储,而不使用int数组...

python 对于提供给Google的位置,返回lat long

java 方法返回值类型 Long与long

为啥 int 存在于 C 中,为啥不只是 short 和 long

为啥 InterlockedCompareExchange 不返回更改的值?