CUDA 文档中引用的 rint() 描述不正确。使用浮点结果舍入到整数映射 IEEE-754 (2008) 指定的舍入模式,如下所示:
trunc() // round towards zero
floor() // round down (towards negative infinity)
ceil() // round up (towards positive infinity)
rint() // round to nearest or even (i.e. ties are rounded to even)
round() // round to nearest, ties away from zero
通常,这些函数按照 C99 标准中的描述工作。对于 rint(),标准指定函数根据当前舍入模式进行舍入(默认舍入到最接近或偶数)。由于 CUDA 不支持动态舍入模式,因此定义为使用当前舍入模式的所有函数都使用舍入模式“舍入到最接近或偶数”。以下是一些示例,显示了 round() 和 rint() 之间的区别:
argument rint() round()
1.5 2.0 2.0
2.5 2.0 3.0
3.5 4.0 4.0
4.5 4.0 5.0
round() 可以按照您发布的代码行相当容易地模拟,我不知道 rint() 的简单模拟。请注意,您不希望使用中间转换为整数,因为“int”支持的数字范围比“double”精确表示的整数更窄。而是酌情使用 trunc()、ceil()、floor()。
由于 rint() 是当前 C 和 C++ 标准的一部分,我有点惊讶 MSVC 不包含这个函数;我建议检查 MSDN 以查看是否提供替代品。如果您的平台支持 SSE4,则可以使用中_mm_round_sd(), _mm_round_pd()
定义的 SSE 内在函数smmintrin.h
,将舍入模式设置为_MM_FROUND_TO_NEAREST_INT
,来实现 CUDA rint() 的功能。
虽然(根据我的经验),SSE 内在函数可以跨 Windows、Linux 和 Mac OS X 移植,但您可能希望避免使用特定于硬件的代码。在这种情况下,您可以尝试以下代码(经过轻微测试):
double my_rint(double a)
{
const double two_to_52 = 4.5035996273704960e+15;
double fa = fabs(a);
double r = two_to_52 + fa;
if (fa >= two_to_52) {
r = a;
} else {
r = r - two_to_52;
r = _copysign(r, a);
}
return r;
}
请注意,MSVC 2010 似乎也缺少标准的 copysign() 函数,所以我不得不替换为 _copysign()。上面的代码假定当前的舍入模式是舍入到最近的偶数(默认情况下)。通过添加 2**52 它确保舍入发生在整数单位位。请注意,这还假设执行纯双精度计算。在对中间结果使用更高精度的平台上,可能需要将 'fa' 和 'r' 声明为 volatile。