例如,由于浮点数的精度,下面的代码将给出不需要的结果。
double a = 1 / 3.0;
int b = a * 3; // b will be 0 here
我想知道如果我使用数学函数会不会出现类似的问题。例如
int a = sqrt(4); // Do I have guarantee that I will always get 2 here?
int b = log2(8); // Do I have guarantee that I will always get 3 here?
如果不是,如何解决这个问题?
编辑:
实际上,我在为算法任务编程时遇到了这个问题。我想得到
所以圆函数不能解决我的问题。我知道我可以通过一个循环来解决这个问题,但似乎不是很优雅。
我想知道是否
int a = pow(2, static_cast<int>(log2(N)));
总能给出正确的结果。例如,如果 N==8,log2(N) 是否有可能给我类似 2.9999999999999 的结果,最终结果变成 4 而不是 8?
最佳答案
不准确的操作数与不准确的结果
实际上,对于基本操作(包括 log2(8)
),不存在阻止 *
为 3 的问题。但它存在于 log2
函数中。
您混淆了两个不同的问题:
double a = 1 / 3.0;
int b = a * 3; // b will be 0 here
在上面的示例中,
a
不完全是 1/3,因此 a*3
可能不会产生 1.0
。该产品可能已经四舍五入到 1.0
,它只是没有。但是,如果 a
不知何故正好是 1/3,那么 a
乘以 3 的乘积就是 1.0
,因为 这就是 IEEE 754 浮点运算的方式 :基本运算的结果是最接近数学运算的可表示值对相同操作数进行相同操作的结果。当确切的结果可以表示为浮点数时,您就可以得到该表示。sqrt 和 log2 的准确性
sqrt
是“基本操作”的一部分,因此在 IEEE 754 系统 中,sqrt(4)
始终保证 始终为 2.0
。log2
不是基本操作的一部分。 IEEE 754 标准不保证此函数的实现结果与数学结果最接近。它可以是更远的另一个可表示的数字。因此,如果没有更多关于您使用的 log2
函数的假设,就不可能知道 log2(8.0)
可以是什么。但是,大多数基本函数(例如
log2
)的合理质量的实现都保证实现的结果在数学结果的 1 ULP 以内。当数学结果不可表示时,这意味着上面或下面的可表示值(但不一定是两者中最接近的一个)。当数学结果完全可表示时(例如 3.0
),则此表示仍然是唯一保证返回的表示。所以关于
log2(8)
,答案是“如果你有一个合理质量的 log2
实现,你可以期望结果是 3.0`”。不幸的是,并非每个基本功能的每个实现都是高质量的实现。请参阅此 blog post ,这是由于在计算
pow
时,广泛使用的 pow(10.0, 2.0)
实现不准确超过 1 个 ULP,从而返回 99.0
而不是 100.0
。四舍五入到最接近的整数
接下来,在每种情况下,您都通过隐式转换将浮点数分配给
int
。这种转换在 C++ 标准中定义为截断浮点值(即向零舍入)。如果您希望浮点计算的结果是一个整数,则可以在分配之前将浮点值四舍五入为 最接近的 整数。在误差不累积到大于 1/2 的值的所有情况下,它将有助于获得所需的答案:int b = std::nearbyint(log2(8.0));
以对问题的直接回答结束标题:是的,在使用浮点函数以产生完整的最终结果时,您应该担心准确性。这些函数甚至没有提供基本操作的保证。
关于c++ - 当我使用带有整数的 C++ 数学函数时,我应该担心精度吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/28779882/