log base 2 c ++中的精度错误

时间:2013-11-05 16:49:25

标签: c++ floating-point precision division

请解释下面给出的代码的输出。对于这两种情况,我得到不同的c值,即

案例1:n的值取自标准输入。 情况2:n的值直接写在代码中。 链接:http://www.ideone.com/UjYFQd

#include <iostream>
#include <cstdio>
#include <math.h>

using namespace std;

int main()
{
    int c;
    int n;

    scanf("%d", &n);    //n = 64
    c = (log(n) / log(2));
    cout << c << endl;  //OUTPUT = 5

    n = 64;
    c = (log(n) / log(2));
    cout << c << endl;  //OUTPUT = 6

    return 0;
}

3 个答案:

答案 0 :(得分:4)

第一次计算log(n)/log(2),结果非常接近6但略低。这就是浮点计算的工作原理:log(64)和log(2)在二进制浮点中都没有无限精确的表示,因此你可以期望将一个除以另一个的结果与真正的数学值略有偏差。 。根据实施情况,您可以获得5或6。

在第二次计算中:

n = 64;
c = (log(n) / log(2));

分配给c的值可以推断为编译时常量,可以由编译器计算。编译器在运行时在与程序不同的环境中进行计算,因此您可以期望在编译时和运行时执行的计算略有不同。

例如,为x86生成代码的编译器可以选择使用使用80位浮点运算的x87浮点指令,而编译器本身使用标准的64位浮点运算来计算编译时常量。

检查编译器的汇编器输出以确认这一点。使用GCC 4.8我从两次计算中得到6。

答案 1 :(得分:4)

由于浮点数的存储方式,您可能会看到这一点:

double result = log(n) / log(2); // where you input n as 64
int c = (int)result; // this will truncate result.  If result is 5.99999999999999, you will get 5

当你对值进行硬编码时,编译器会为你优化它:

double result = log(64) / log(2); // which is the same as 6 * log(2) / log(2)
int c = (int)result;

很可能完全取代:

int c = 6;

因为编译器会看到你正在使用一堆编译时常量来将值存储在一个变量中(它将继续并在编译时处理该值)。

如果您想获得该操作的整数结果,您应该使用std::round而不是仅仅转换为int

int c = std::round(log(n) / log(2));

答案 2 :(得分:1)

输出的差异可以解释为gcc在常量情况下优化对log的调用这一事实,例如,在这种情况下:

n = 64;
c = (log(n) / log(2));

log的两次调用都是在编译时完成的,这些编译时评估会导致不同的结果。这在Other Built-in Functions Provided by GCC部分的gcc手册中有说明:

  

GCC包含标准C库中许多功能的内置版本。即使您指定-fno-builtin选项,前缀为_builtin的版本也始终被视为与C库函数具有相同的含义。 (参见C方言选项)许多这些功能仅在某些情况下得到优化; 如果在特定情况下未进行优化,则会发出对库函数的调用。

log是内置版本的众多功能之一。如果我使用-fno-builtin进行构建,则会对log进行四次调用,但如果没有,则只会调用一次log,您可以通过使用-S标记进行构建来检查输出gcc生成的程序集。