大浮点数的 OpenCL 缩减结果错误

OpenCL reduction result wrong with large floats

我使用 AMD's two-stage reduction example 使用浮点精度计算从 0 到 65 536 的所有数字的总和。不幸的是,结果不正确。但是,当我修改我的代码,以便计算 65 536 个较小数字(例如 1)的总和时,结果是正确的。

我在代码中找不到任何错误。由于 float 类型,我是否有可能得到错误的结果?如果是这种情况,解决问题的最佳方法是什么?

这是使用有限精度 CPU 或 GPU 对浮点数求和的 "side effect"。准确性取决于算法和值求和的顺序。背后的理论和实践在 Nicholas J, Higham 的论文中解释

浮点求和的精度

http://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=7AECC0D6458288CD6E4488AD63A33D5D?doi=10.1.1.43.3535&rep=rep1&type=pdf

解决方法是使用更智能的算法,例如 Kahan 求和算法

https://en.wikipedia.org/wiki/Kahan_summation_algorithm

海厄姆论文也有一些替代方案。

这个问题说明了基准测试的本质,基准测试的第一条规则是获得 正确答案,使用真实数据!

您的内核或主机应用程序的编码可能没有错误。问题出在单精度浮点数上。

正确的和是:65537 * 32768 = 2147516416,用二进制表示需要 31 位 (10000000000000001000000000000000)。 32 位浮点数最多只能精确保存 2^24 的整数。

"Any integer with absolute value less than [2^24] can be exactly represented in the single precision format" "Floating Point" article, wikipedia

这就是当总和小于或等于 2^24 时得到正确总和的原因。如果您使用单精度进行完整求和,那么无论您在哪个设备上执行内核,您最终都会失去准确性。您可以采取一些措施来获得正确答案:

  • 如果您的平台支持,请使用 double 而不是 float
  • 使用 int 或 unsigned int
  • 对较小的一组数字求和,例如:0+1+2+...+4095+4096 = (2^23 + 2^11)

Read more about single precision here.