2

请解释以下给定代码的输出。对于这两种情况,我都得到了不同的 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;
}
4

3 回答 3

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。

于 2013-11-05T17:01:45.627 回答
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));
于 2013-11-05T17:02:21.527 回答
1

输出的差异可以通过gcc优化常量情况下的调用来解释log,例如,在这种情况下:

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

两个调用log都是在编译时完成的,这些编译时评估可能会导致不同的结果。这在GCC 提供的其他内置函数部分的 gcc 手册中有记录,其中说:

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

并且log是具有内置版本的众多功能之一。如果我使用-fno-builtin所有四个调用进行构建log,但没有log发出一个调用,则可以通过使用标志进行构建来检查这一点,该-S标志将输出gcc生成的程序集。

于 2013-11-05T17:14:55.027 回答