从std::round转换为int是否安全

Is it safe to cast to int from std::round?

本文关键字:int 是否 安全 转换 std round      更新时间:2023-10-16

我有一个关于std::round with signature的问题:double round (double x);

假设我有这个代码:

int i = std::round(0.9);

在这种情况下,std::round应该返回1.00000000000,但这与0.9999999999999非常接近,我担心浮点错误最终会将其四舍五入。

我希望i == 1,但这有保证吗?

std::round函数返回一个浮点值,">从零开始舍入一半大小写"。与任何doubleint的隐式转换一样,编译器将发出警告:

从'double'到'int'的转换,可能丢失数据

如果要返回整数值,请使用std::lround或std::lrint。

是。只要你不溢出接收类型。

您在这里没有任何顾虑:std::round(0.9)将精确地四舍五入到1.0,因此i == 1是有保证的

该标准坚持使用返回的double所假定的最接近的整数值。

请注意,对于IEEE754double,超过2的52次方的所有值都是整数值!其推论是,您的四舍五入候选数已经为整数值,因此函数减少为no-op。因此,例如,std::round(4503599627370496.5)将返回4503599627370496的事实完全与4503599627370496.5最初不能表示为double的事实有关。

作为最后的技术要点,请注意,std::round表现得非常好,部分原因是a.5形式的任何数字(四舍五入中的截断点)都是并矢有理,因此可以精确地用二进制浮点表示。这就是为什么另一种方法,如添加0.5和截断,可能会引入错误,因为如果这样做,可能会出现笑话数字。

C++标准在这方面遵循C标准。在N1570(~C11)中,round的描述如下:

round函数以浮点格式将其参数四舍五入到最接近的整数值,从零开始取整一半大小写,而不考虑当前的取整方向。

然而,正如Ron所指出的,像lrint这样的函数正是您想要的。

根据cppreference:

浮动-积分转换

浮点类型的prvalue可以转换为任何整数类型。分数部分被截断,即小数部分被丢弃。如果该值不适合destination类型,行为未定义(即使在destination类型是无符号的,模运算不适用)。如果目标类型是bool,这是一个布尔转换(见下文)。

尽管这并不能证明它在所有平台上都是真的,但您可以尝试四舍五入所有可能的浮点值,看看会发生什么:

#include <cmath>
#include <iostream>
#include <limits>
int main() {
// Define the range of valid 'int' values
const float mi = std::numeric_limits<int>::min();
const float ma = std::numeric_limits<int>::max();
float maxd = -std::numeric_limits<float>::infinity();
std::size_t k = 0;
// Iterate over all float point values between 'mi' and 'ma'
for (float f = mi; f < ma; f = std::nextafter(f, ma), ++k) {
// Static cast after round
auto i = static_cast<int>(std::round(f));
// Convert back to float and check the difference
float d = std::abs(static_cast<float>(i) - f);
// Store the largest recorded difference
if (d > maxd) {
maxd = d;
}
// Print progress to the console, just to see it's not stuck
if (k % 1321 == 0) {
std::cout << k << ": " << f << "               r";
}
}
std::cout << std::endl;
// Prints 0.5 for me
std::cout << maxd << std::endl;
}

如果你担心可能发生的事情确实发生了,上面的程序会打印一个大于0.5的值。

如何添加0.5,然后强制转换为int(截断)

if(x < 0) { //number is negative
return (int) x - 0.5;
} else {
return (int) x + 0.5;//number is positive
}

在您的示例中,它肯定会返回1因为1.49999999999强制转换为int是1