【问题标题】:Weird C program behaviour奇怪的 C 程序行为
【发布时间】:2017-01-17 22:32:07
【问题描述】:

我有以下 C 程序:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>

int main() {
    const int opt_count = 2;

    int oc = 30;

    int c = 900;

    printf("%d %f\n", c, pow(oc, opt_count));
    assert(c == (int)(pow(oc, opt_count)));
}

我在 Windows 8.1 上运行 MinGW。 Gcc 版本 4.9.3。我编译我的程序:

gcc program.c -o program.exe

当我运行它时,我得到这个输出:

$ program
900 900.000000
Assertion failed: c == (int)(pow(oc, opt_count)), file program.c, line 16

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

发生了什么事?我希望断言能够通过,因为 900 == 30^2。

谢谢!

编辑

我没有使用任何分数或小数。我只使用整数。

【问题讨论】:

  • 如果将%f 替换为%.20f 会发生什么?
  • 每个程序员的...等等等等
  • @Olaf 我知道计算机不能用二进制浮点表示 0.1。但它们当然可以完全代表 3、30、900 等。
  • @herugar5 那么中间计算呢?还有如何你知道900可以精确表示吗?

标签: c math gcc mingw assertion


【解决方案1】:

pow 的实现是通过时会发生这种情况

pow(x,y) = exp(log(x)*y)

其他库实现首先通过整数幂来减少指数,从而避免这种小的浮点错误。


更多涉及的实现包含如下步骤

pow(x,y) {
    if(y<0) return 1/pow(x, -y);

    n = (int)round(y);
    y = y-n;
    px = x; powxn = 1;
    while(n>0) {
        if(n%2==1) powxn *= px;
        n /=2; px *= px;
    }
    return powxn * exp(log(x)*y);
}

与通常的分而治之分别。整数幂 powxn 的减半平方方法。

【讨论】:

  • 该函数没有合规声明。如所写,它返回一个int 结果。 int 的演员表是错误的。 int 不能代表 double 可以的所有值。仅供参考:y 无论如何都必须是整数值,请参阅标准。
【解决方案2】:

@LutzL 给你一个很好的答案(和解决方案),另一个解决方案是将差异与 epsilon 进行比较,例如:0.00001,这样你就可以使用 math.h 中包含的标准函数 pow

#define EPSILON 0.0001
#define EQ(a, b) (fabs(a - b) < EPSILON)

assert(EQ((double)c, pow(oc, opt_count)));

【讨论】:

  • 没有很好的方法来确定什么是合适的 epsilon 值,您可以轻松掩盖实际的小值差异。如果您想将值提高到 2 次方,只需将其乘以自身,而不是调用 pow()
  • @KeithThompson,我同意在这种情况下30. * 30. 就足够了,但功率值可以来自用户输入
猜你喜欢
  • 1970-01-01
  • 2020-09-03
  • 2015-01-07
  • 1970-01-01
  • 1970-01-01
  • 2014-05-11
  • 2017-01-02
  • 1970-01-01
相关资源
最近更新 更多