【问题标题】:rint not present in Visual Studio 2010 math.h and equivalent of CUDA rintrint 在 Visual Studio 2010 math.h 中不存在,相当于 CUDA rint
【发布时间】:2013-02-01 21:25:19
【问题描述】:

我正在将 CUDA 代码移植到 C++ 并使用 Visual Studio 2010。CUDA 代码使用 rint 函数,该函数似乎不存在于 Visual Studio 2010 math.h 中,所以我似乎需要自己实现。

根据这个link,CUDA rint函数

以浮点格式将 x 舍入为最接近的整数值,中途情况舍入为零。

我想我可以使用转换为 int 来丢弃小数部分,有效地向零舍入,所以我最终得到了以下函数

inline double rint(double x)
{
    int temp; temp = (x >= 0. ? (int)(x + 0.5) : (int)(x - 0.5));
    return (double)temp;
}

它有两种不同的类型,一种是int,一种是double

我有三个问题:

  1. 对于“小”数字,上述函数是否完全等同于 CUDA rint?对于无法表示为 int 的“大”数字,它会失败吗?
  2. 定义rint 是否有任何计算效率更高的方法(而不是使用两个转换)?

非常感谢您。

【问题讨论】:

  • 我怀疑你需要强制转换为 int,而且它很容易出错。改用地板/天花板?
  • 您的问题已经得到解答,njuffa 的回答可能是最好的rint() 实现,但我应该指出+ 0.5 版本存在更多问题,而不仅仅是@987654334 的溢出@ 不能表示为 int 的参数。使用严格的 IEEE 754 编译器,0.49999999999999994 和 2^52 和 2^53 之间的一半数字的答案是不正确的。请参阅blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1(对于float,解释很容易翻译成double)。
  • 如果您的计算机上的 int 是 32 位,则上述实现对于大于 2^31 的数字将失败。使用 int64_t 代替 int 会有所帮助,但当 x 是大双精度(大于 2^63)时仍然会出现问题。

标签: c++ visual-studio-2010 cuda


【解决方案1】:

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,您可以使用在 smmintrin.h 中定义的 SSE 内在函数 _mm_round_sd(), _mm_round_pd(),并将舍入模式设置为 _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。

【讨论】:

  • 谢谢njuffa。一如既往的称职和准确的答案!我也很惊讶 Visual Studio 2010 不支持rint,但这个post 似乎支持这一点。我已经测试了您的my_rint 代码并且工作正常。是否应该提交错误报告来修复 CUDA 文档?
  • 我已经提交了一个错误来修复 CUDA 文档中有关 rint() 的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-30
  • 2013-10-23
  • 2023-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多