【问题标题】:Comparing two equal unsigned long in C evaluates to false在 C 中比较两个相等的 unsigned long 计算结果为 false
【发布时间】:2016-06-21 20:25:47
【问题描述】:

我是 C 的新手,正在玩弄它。所以我最终实现了斐波那契代码(迭代和递归)。我写了一个测试函数,它应该给我一个绿色(我的实现工作)或红色。它说我得到了正确的返回值,但它的状态是红色的。这两个值都应该是 unsigned long。我正在使用 make 在 OSX 上编译

#include <stdio.h>

unsigned long fibonacci(unsigned long n);
void test_fibonacci(unsigned long n, unsigned long assertion);

int main(int argc, char* argv[])
{
    test_fibonacci(1, 1);
    test_fibonacci(2, 1);
    test_fibonacci(3, 2);
    test_fibonacci(10, 55);
    return 0;
}

unsigned long fibonacci(unsigned long n)
{
    unsigned long result = 1;
    unsigned long lastResult;
    for (unsigned long i = 2; i <= n; i++)
    {
        // save the current result to save it as the lastResult after this iteration
        unsigned long lastResultTmp = result;
        result = lastResult + result;
        lastResult = lastResultTmp;
    }
    return result;
}

void test_fibonacci(unsigned long n, unsigned long assertion)
{
    printf(
        "fibonacci(%lu): %lu    | %s | asserted: %lu\n",
        n,
        fibonacci(n),
        (fibonacci(n) == assertion) ? "green" : "red",
        assertion
    );
}

我的 Makefile

CFLAGS=-Wall -g

all: main

clean:
    rm -f main
    rm -Rf *.dSYM

输出:

fibonacci(1): 1    | green | asserted: 1
fibonacci(2): 1    | red | asserted: 1
fibonacci(3): 2    | red | asserted: 2
fibonacci(10): 55    | red | asserted: 55

【问题讨论】:

    标签: c comparison long-integer unsigned


    【解决方案1】:

    我没有得到你的输出。这是我所看到的:

    fibonacci(1): 1    | green | asserted: 1
    fibonacci(2): 2    | red | asserted: 1
    fibonacci(3): 4    | red | asserted: 2
    fibonacci(10): 3353    | red | asserted: 55
    

    我很好奇为什么,所以我用 valgrind 运行它。那很快就弹出了这个错误:

    ==5619== Conditional jump or move depends on uninitialised value(s)
    ==5619==    at 0x4005E7: test_fibonacci (fibonacci.c:31)
    ==5619==    by 0x400559: main (fibonacci.c:9)
    

    所以看起来这与读取未初始化的变量有关,这会给您错误的值。这最终将我们指向这里:

    unsigned long fibonacci(unsigned long n)
    {
        unsigned long result = 1;
        unsigned long lastResult; // <---- LOOK HERE
        for (unsigned long i = 2; i <= n; i++)
        {
            // save the current result to save it as the lastResult after this iteration
            unsigned long lastResultTmp = result;
            result = lastResult + result;
            lastResult = lastResultTmp;
        }
        return result;
    }
    

    注意lastResult 未初始化,但在行中读取

     result = lastResult + result;
    

    所以看起来您需要初始化该值。由于该值对应于先前的斐波那契数,您应该初始化为零。这样做会导致所有测试通过。

    现在,究竟发生了什么让你看起来好像得到了正确的答案但仍然失败了?请注意,您在测试代码中调用了两次fibonacci。我的猜测是对fibonacci 的第一次调用——被打印出来的那个——只是偶然的机会碰巧正常工作,因为出于某种原因,第一次调用时lastResult 中的值恰好是0。但是,我'我会猜测对fibonacci第二次 调用(与预期结果进行比较的那个)没有返回与第一次调用相同的值,因为无论出于何种原因,@ 的值进行第二次呼叫时,987654331@ 不是 0。这就是未定义行为的问题 - 可能会发生这种奇怪的事情!

    【讨论】:

    • 非常感谢!这是完全正确的。由于第一个斐波那契数,我将 for 循环中的 i 设置为 3 和 lastResult 为 1。这解决了我的问题。我完全必须检查 valgrind。
    • @noeden Valgrind 是您入门时的绝佳工具。我的建议是 (1) 编译时将警告设置一路调高,(2) 将警告转化为错误,以及 (3) 在 Valgrind 中运行程序。你会惊讶于你会以这种方式发现多少错误。 :-)
    • 同意初始化lastResult 的需要,因为不这样做会导致UB。然而,我怀疑其他东西导致 OP 得到“正确”的答案,但比较失败。嗯。 UB就是UB。 --> 啊哈,fibonacci() 每隔一段时间就为 OP 报告正确答案。
    • @chux 我刚刚更新了我的答案,对此行为进行了合理的解释。你怎么看?
    • 同意 - 一个真正奇怪的 UB。正如here 指出的那样,它确实指出了 OP 测试中的一个弱点。 OTOH,我对 OP 一开始就有一个合理的好测试代码印象深刻。紫外线给你和 OP。
    【解决方案2】:

    斐波那契(1): 1 |绿色 |断言:1

    斐波那契 (2): 3076653057 |红色 |断言:1

    斐波那契 (3): 3076653058 |红色 |断言:2

    斐波那契 (10): 1526988855 |红色 |断言:55

    我正在将此输出添加到您的代码中。我认为这是因为未初始化的变量 lastResult。因为当我用 0 初始化它时,我得到了正确的结果。

    【讨论】:

      【解决方案3】:

      操作see comment

      这表明您的测试存在一个微妙的弱点。当fibonacci(n) 被调用时,它提供了正确的答案。当(fibonacci(n) == assertion) 被调用时,它提供了错误的答案。弱点是您的测试代码每次测试调用fibonacci(n) 两次而不是一次。由于您的代码有一个未初始化的变量:@templatetypedef

      // unsigned long lastResult;  // bad
      unsigned long lastResult = 0; // good
      

      使用 UB(未定义行为)- 这是可能的。


      测试代码应该调用过测试函数一次

      unsigned long f = fibonacci(n);  
      printf("fibonacci(%lu): %lu    | %s | asserted: %lu\n", 
          n, f, (f == assertion) ? `"green" : "red", assertion;
      

      那么至少,使用错误的test_fibonacci(),获得一致结果的可能性要大得多。


      OTOH,这个弱点指出可能是一个优势,因为如果每个循环只调用一次 test_fibonacci() 测试,UB 可能不会以不好的方式表现出来。

      【讨论】:

      • 谢谢!我要改变它。
      猜你喜欢
      • 2010-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-03
      • 2019-06-19
      • 2011-10-01
      • 1970-01-01
      相关资源
      最近更新 更多