std::cbrt 的舍入错误?

Posted

技术标签:

【中文标题】std::cbrt 的舍入错误?【英文标题】:Rounding error of std::cbrt? 【发布时间】:2012-12-27 22:10:39 【问题描述】:

不知道是否应该将以下内容报告为标准库的gcc实现中的错误。

对于所有无符号整数i,如果我们将int(std::sqrt(i)) 与整数的实际平方根进行比较,转换总是会得到好的结果。如果我们对std::cbrt 做同样的事情,情况并非如此:

// Problem of rounding of std::cbrt for i from 0 to 100 million
// i, exact cbrt(i), result of int(std::cbrt(i))
2197, 13, 12
17576, 26, 25
24389, 29, 28
140608, 52, 51
185193, 57, 56
195112, 58, 57
226981, 61, 60
1092727, 103, 102
1124864, 104, 103
1442897, 113, 112
1481544, 114, 113
1560896, 116, 115
1685159, 119, 118
1815848, 122, 121
8741816, 206, 205
8869743, 207, 206
8998912, 208, 207
9393931, 211, 210
9938375, 215, 214
11543176, 226, 225
11852352, 228, 227
12487168, 232, 231
12649337, 233, 232
13481272, 238, 237
13651919, 239, 238
14348907, 243, 242
14526784, 244, 243
14706125, 245, 244
69426531, 411, 410
69934528, 412, 411
70957944, 414, 413
71991296, 416, 415
72511713, 417, 416
73560059, 419, 418
74618461, 421, 420
75151448, 422, 421
79507000, 430, 429
88121125, 445, 444
89314623, 447, 446
91733851, 451, 450
92345408, 452, 451
92959677, 453, 452
94818816, 456, 455
99897344, 464, 463 

您认为应该将其报告为缺陷吗?

【问题讨论】:

这可能是因为输入值在实际立方根之前进行浮点转换,而立方根复合了增量? 尝试舍入而不是截断。 @WhozCraig:显示范围内的整数没有表示错误(数据类型为double)。 你用的是什么版本?这在 GCC 4.3.4 中似乎很好:ideone.com/JRcWpx. 顺便说一句,每当您在代码中得到意外结果时,立即假设您的代码是错误的 - 在大多数情况下,您极不可能偶然发现编译器错误。 【参考方案1】:

std::cbrt 返回浮点类型(float、double 等),但您将其转换为 int。这种转换会截断而不是四舍五入,例如0.9999 变为 0。虽然 2197 的立方根是整数似乎是合乎逻辑的,但由于浮点类型以二进制形式存储,因此并不总是可以完美地表示十进制数,这种不准确性很可能在std::cbrt 执行的计算期间传播。例如,如果 std::cbrt(2197) == 12.99999(我的编译器不支持它,所以我无法检查实际值),那么通过将其转换为 int,您会将值截断为 12。

要更正您的代码,请先将std::cbrt(i) 的结果四舍五入,然后再将其转换为int。请参阅this question 了解如何执行此操作。

【讨论】:

这都是合理的。剩下的问题是:cbrt(2197) 是否允许返回12.999999 用浮点数精确表示小整数绝对没有问题; 132197 都具有精确的浮点表示,即使是 float。问题是cbrt 与 OP 使用的任何标准 c 库的计算有点不准确。解决方案(对于整数立方根)如您所建议的那样。 @rici 我没有说精确表示整数有问题 - 我说的是十进制数(包括非整数)。 @JonBentley:是的,但是 2197 的立方根正好是 13。所以如果它被精确计算,就不会有表示问题。 @JonBentley:如果要求结果正确,那将是一个微不足道的借口。 IEEE-754 要求平方根是正确的(也就是说,结果必须是参数的精确平方根的正确舍入表示),而数学库通常可以做到这一点。 cbrt 没有这样的保证,数学库可能会偏离 1 个 ULP(或更多,据我所知,但 1 个 ULP 很常见),就像三角函数一样。

以上是关于std::cbrt 的舍入错误?的主要内容,如果未能解决你的问题,请参考以下文章

如何处理 vb.net 中双精度数的舍入错误?

在 C++ 中使用 floor 函数的舍入错误

为啥 iOS 自动布局会导致预 Retina 显示器出现明显的舍入错误(包括单元测试)

GCC 不断抱怨 AVX512 函数 _mm512_cvt_roundpd_epi64 的“错误:不正确的舍入操作数”

java中的舍入双[重复]

将浮点数转换为无符号短时的舍入问题