当我使用带整数的C++数学函数时,我是否应该担心精度?

del*_*rst 4 c++ algorithm math floating-point floating-accuracy

例如,由于浮点数的精度,下面的代码会产生不希望的结果.

double a = 1 / 3.0;
int b = a * 3;      // b will be 0 here
Run Code Online (Sandbox Code Playgroud)

我想知道如果我使用数学函数会出现类似的问题.例如

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?
Run Code Online (Sandbox Code Playgroud)

如果没有,如何解决这个问题?

编辑:

实际上,当我为算法任务编程时,我遇到了这个问题.在那里,我想得到

最大的整数,是2的幂,小于或等于整数N.

所以圆函数无法解决我的问题.我知道我可以通过循环来解决这个问题,但它似乎不是很优雅.

我想知道是否

int a = pow(2, static_cast<int>(log2(N)));
Run Code Online (Sandbox Code Playgroud)

总能给出正确的结果.例如,如果N == 8,log2(N)有可能给出类似于2.9999999999999的内容,最终结果是4而不是8吗?

Pas*_*uoq 8

不准确的操作数与不准确的结果

我想知道如果我使用数学函数会出现类似的问题.

实际上,log2(8)基本操作(包括*)不存在可能阻止为3 的问题.但它存在于该log2功能中.

你混淆了两个不同的问题:

double a = 1 / 3.0;
int b = a * 3;      // b will be 0 here
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,a不完全是1/3,所以有可能a*3不产生1.0.该产品可能碰巧到了1.0,但事实并非如此.但是,如果a不知何故恰好是1/3,那么a3 的乘积就完全正确1.0,因为这就是IEEE 754浮点的工作原理:基本运算的结果是相同运算的数学结果的最接近的可表示值.在同一个操作数上.如果确切的结果可以表示为浮点数,那么表示就是你得到的.

sqrt和log2的准确性

sqrt是"基本操作"的一部分,所以sqrt(4)保证始终,没有例外,在IEEE 754系统,是2.0.

log2不属于基本操作的一部分.IEEE 754标准不保证实现此功能的结果最接近数学结果.它可以是更远的另一个可表示的数字.因此,如果没有关于log2您使用的函数的更多假设,则无法确定log2(8.0)可能的内容.

然而,大多数基本功能的合理质量的实现,例如log2保证实现的结果在数学结果的1 ULP内.当数学结果不可表示时,这意味着上面的可表示值或下面的值(但不一定是两者中最接近的一个).当数学结果完全可以表示时(例如3.0),那么这个表示仍然是唯一保证返回的表示.

所以log2(8),答案是"如果你有一个合理的质量实现log2,你可以期望结果是3.0`".

不幸的是,并非每个基本功能的每个实现都是高质量的实现.请参阅此博客文章,这是由计算时广泛使用的pow超过1 ULP的不准确实现引起的pow(10.0, 2.0),因此返回99.0而不是100.0.

舍入到最接近的整数

接下来,在每种情况下,将浮点分配给int具有隐式转换的a.此转换在C++标准中定义为截断浮点值(即向零舍入).如果希望浮点计算的结果为整数,则可以在分配浮点值之前将浮点值舍入为最接近的整数.在错误未累积到大于1/2的值的所有情况下,它将有助于获得所需的答案:

int b = std::nearbyint(log2(8.0));
Run Code Online (Sandbox Code Playgroud)

最后简单回答问题的标题:是的,在使用浮点函数产生积分的最终结果时,你应该担心准确性.即使有基本操作的保证,这些功能也不会出现.