【问题标题】:Going crazy, why are my variables changing on me?发疯了,为什么我的变量会改变我?
【发布时间】:2011-09-15 13:20:20
【问题描述】:

好的,我之前遇到过这种情况,变量会因为内存分配问题或错误寻址等而随机更改数字,例如当您超出数组范围时。但是,我没有使用数组、指针或地址,所以我不知道为什么在执行此循环后它突然决定设置为0 后的“指数”等于循环内的288

编辑:它决定具体中断:0x80800000

这不会在一个测试中中断,我们有一个“测试”客户端,它遍历几个测试用例,每次它再次调用它,每次再次调用函数时,值应该是设置为等于它们的原始值。

/* 
 * float_i2f - Return bit-level equivalent of expression (float) x
 *   Result is returned as unsigned int, but
 *   it is to be interpreted as the bit-level representation of a
 *   single-precision floating point values.
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 30
 *   Rating: 4
 */
unsigned float_i2f(int x) { 
    int sign= 0;
    int a=0;
    int exponent=0;
    int crash_test=0;
    int exp=0;
    int fraction=0;
    int counter=0;

    if (x == 0) return 0;
    if (!(x ^ (0x01 << 31)))
    {
        return 0xCF << 24;
    }
    if (x>>31)
    {
        sign = 0xFF << 31;
        x = (~x) + 1;
    }
    else
    {
        sign = 0x00;
    }
    //printf(" After : %x  ", x);

    a = 1;
    exponent = 0;
    crash_test = 0;
    while ((a*2) <= x)
    {
        if (a == 0) a =1;
        if (a == 1) crash_test = exponent;
        /*
        if(exponent == 288) 
        {exponent =0;
            counter ++;
            if(counter <=2)
            printf("WENT OVERBOARD WTF %d  ORIGINAL %d", a, crash_test);
        }
        */
        if (exponent > 300) break;

        exponent ++;
        a *= 2;
    }

    exp = (exponent + 0x7F) << 23;
    fraction = (~(((0x01)<< 31) >> 7)) & (x << (25 - (exponent + 1)));
    return sign | exp | fraction;
}

【问题讨论】:

  • 我会在调试代码的过程中添加cout &lt;&lt;进度,因为我看不懂。
  • 这是会产生错误的最小示例吗?
  • 在 288 次迭代之后,正是它将等于...
  • 导致这种情况的 x 的违规值是什么?它只发生在 x 的一个值上吗?
  • 嗯,你知道你只是在循环中循环并增加指数,所以最终它会达到 288。它可能会发生得非常快,因为这些天循环很快(没有更多使用仓鼠的机械计算器旋转巴贝奇时代使用的齿轮)。

标签: c++ c debugging testing


【解决方案1】:

使用调试器或 IDE,在指数值上设置观察/断点/断言(例如 (exponent &gt; 100)

float_i2f() 调用的 x 的违规值是什么?是否所有 x 或某个范围的指数都爆炸了?

(您刚刚说 x = 0x80800000 时吗?您是否在指数上设置了监视并在调试器中为该值单步执行?应该回答您的问题。例如,您是否检查过 0x807FFFFF 是否有效?)

【讨论】:

  • 补充一点,几乎没有什么突然发生。如果这是一个可重复的错误,那么你在错误的时间做同样的事情。您需要记录或观察发生的情况以查看代码在哪里发生故障。
  • @madmik3 这正是我刚刚对他说的 :) 需要更多信息。
  • 基本上我说的是yes x = 0x80800000 发生这种情况时,现在尝试在visual studio中测试它,我之前使用gdb使用unix,老师为我们提供了一个测试用例程序。
【解决方案2】:

我自己用 Visual Studio 试了一下,输入“10”,好像还可以。

问:你能在失败的地方给我一个输入值“x”吗?

问:您使用的是什么编译器?你在哪个平台上运行?

【讨论】:

  • 我相信我们正在使用 gcc,我们在 unix 中“制作”它,然后在其上执行测试客户端。如果我弄错了,测试客户会告诉我。它声称在测试 0x80800000 时它搞砸了,我已将其缩小为无法进行进度,因为 a 设置为 0,并且由于某些上帝预见的原因,指数设置为 288
  • 考虑查看 gdb 以逐步执行代码。 unknownroad.com/rtfm/gdbtut/gdbuse.html
  • 你刚刚回答了你自己的问题 :) "0x80800000" 设置了符号位。当“a”变为负数时会发生什么?而你一直乘以二?哎呀;)
【解决方案3】:

你有一行在你的 while 循环结束时增加指数。

while((a*2) <= x)
{
    if(a == 0) a =1;
    if(a == 1) crash_test = exponent;
    /* 

    if(exponent == 288) 
    {
        exponent =0;
        counter ++;
        if(counter <=2)
        printf("WENT OVERBOARD WTF %d  ORIGINAL %d", a, crash_test);
    }
    */

    if(exponent > 300) break;

    exponent ++;
    a *= 2;

}

【讨论】:

    【解决方案4】:

    变量exponent 没有做任何神秘的事情。您每次通过循环递增exponent,因此它最终会达到您喜欢的任何数字。真正的问题是,为什么您认为应该退出的循环却没有退出?

    您的循环条件取决于a。尝试在循环重复时打印出a 的连续值。在a 到达 1073741824 之后,您是否注意到发生了什么有趣的事情?您听说过您的课程中的整数溢出吗?

    【讨论】:

      【解决方案5】:

      只需处理“a”变为负数的情况(或者更好的是,验证您的输入,这样它就不会首先变为负数),你应该没问题 :)

      【讨论】:

        【解决方案6】:

        那里有许多无用的优化尝试,我已将其删除,以便代码更易于阅读。我还酌情使用了&lt;stdint.h&gt; 类型。

        循环中a *= 2 中存在有符号整数溢出,但主要问题是缺少常量和幻数的奇怪计算。

        这仍然不是示例,因为常量都应该被命名,但这似乎可靠地工作。

        #include <stdio.h>
        #include <stdint.h>
        
        uint32_t float_i2f(int32_t x) { 
            uint32_t sign= 0;
            uint32_t exponent=0;
            uint32_t fraction=0;
        
            if (x == 0) return 0;
            if ( x == 0x80000000 )
            {
                return 0xCF000000u;
            }
            if ( x < 0 )
            {
                sign = 0x80000000u;
                x = - x;
            }
            else
            {
                sign = 0;
            }
        
            /* Count order of magnitude, this will be excessive by 1. */
            for ( exponent = 1; ( 1u << exponent ) <= x; ++ exponent ) ;
        
            if ( exponent < 24 ) {
                fraction = 0x007FFFFF & ( x << 24 - exponent ); /* strip leading 1-bit */
            } else {
                fraction = 0x007FFFFF & ( x >> exponent - 24 );
            }
            exponent = (exponent + 0x7E) << 23;
            return sign | exponent | fraction;
        }
        

        【讨论】:

        • 类的奇怪格式是必要的,什么不是。我不能直接使常量大于 8 位,也不能在我认为的条件下使用“
        • 一直在努力,可能是A值溢出了,看来我可能需要为这种情况设置一个检查条件。
        • 不能使常量大于 8 位????我希望你忘记这门课教给你的任何东西——这段代码中有许多糟糕的做法,这使得它难以验证并且不比可维护的替代方法快。跨度>
        • @oorosco:是的,a 已经溢出,需要成为unsigned。这是一个可行的解决方案。
        • @oorosco:永远不要忘记你的老师是一个可怜的程序员,他需要断言他的 8 位方法是相关的,而不是跟上时代的步伐。如果这是主题,该课程应该使用 8 位汇编语言——我参加了一门使用 8086 asm 的课程并且有一些有趣的问题,但这只是愚蠢的。
        【解决方案7】:

        a 溢出。 a*2==0a==1&lt;&lt;31 时,所以每次exponent%32==0a==0 并且你循环直到exponent==300

        还有一些其他问题:

        exponent&gt;=24 时,您的fraction 计算关闭。负左移不会自动变成正右移。

        生成分数的掩码也略有错误。前导位总是假定为1,尾数只有23位,所以x

            fraction = (~(((0x01)<< 31) >> 8)) & (x << (24 - (exponent + 1)));
        

        abs(x)&gt;=1&lt;&lt;31 时,计算exponent 的循环会失败(如果不适当舍入,会导致精度损失);将隐式 1 考虑在内的循环在这里会更好。

        【讨论】:

          猜你喜欢
          • 2012-10-04
          • 2016-01-27
          • 2019-08-11
          • 1970-01-01
          • 2014-08-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多