mspgcc中的意外结果

时间:2011-05-10 10:58:32

标签: c gcc types unsigned mspgcc

我在C中编写了一个简单的代码,但是当我将它移植到mspgcc时,它并没有给我正确的值。这是我的代码的一部分:

unsigned short int xk=3588, yk=47541, yk1, sig=10, de=1;

unsigned long int xk1;

xk1=( xk+(sig*(yk-xk)*de));

yk1=xk1 % 65535;

我期望的结果是xk1=443118yk1=49908,但在mspgcc中它会给我xk1=yk1=49902。 我不知道类型选择中的故障可能在哪里?

修改

这是我的完整代码

#include <stdio.h>
#include "uart1.h"
#include <stdlib.h> 
#include <stdint.h>
#include <string.h>
#include <math.h>

int putchar(int c)
{
   return uart1_putchar(c);
}

int main(void) 
{
   //variables
   unsigned short int xk=3588, yk=47541, yk1, sig=10, de=1;
   unsigned long xk1;
   uart1_init();
   xk1=( xk+(sig*((unsigned long)yk-xk)*de));
   yk1=xk1 % 65536;
   printf("xk1=%6lx\t\n,",xk1);
   printf("yk1=%u\t\n,",yk1);
}

1 个答案:

答案 0 :(得分:4)

使用此编译器,整数的大小必须为16位,这是一个完全合法的系统。

您预计xk1为443118. 443118%65536为49902。

自计算:

unsigned short int xk=3588, yk=47541, yk1, sig=10, de=1;

unsigned long int xk1;

xk1=( xk+(sig*(yk-xk)*de));

仅涉及unsigned short个值,这些值会提升为unsigned int,然后将结果计算为unsigned int,最后将该值分配给unsigned long。但是多余的位早已丢失......计算是在16位无符号算术中完成的。


实验

在具有GCC 4.1.2的64位RHEL5(AMD x86 / 64)机器上进行。为了模拟一个16位的整数计算,我用((unsigned short)(...))强制转换表达式(第二个副本)。双倍乘法只获得一次演员;无论两次乘法的顺序如何,结果都不会改变(加倍,因为其中一个被乘数是1)。我已经包含(第三个副本)表达式(unsigned long)演员。

测试程序:

#include <stdio.h>

int main(void)
{
    unsigned short int xk=3588, yk=47541, sig=10, de=1;
    unsigned long int xk1;

    xk1 = (xk+(sig*(yk-xk)*de));
    printf("No Cast: %6lu = (%u+(%u*(%u-%u)*%u))\n", xk1, xk, sig, yk, xk, de);
    xk1 = ((unsigned short)(xk+((unsigned short)(sig*((unsigned short)(yk-xk))*de))));
    printf("US Cast: %6lu = (%u+(%u*(%u-%u)*%u))\n", xk1, xk, sig, yk, xk, de);
    xk1 = (xk+(sig*((unsigned long)yk-xk)*de));
    printf("UL Cast: %6lu = (%u+(%u*(%u-%u)*%u))\n", xk1, xk, sig, yk, xk, de);
    return 0;
}

输出结果为:

$ gcc -Wall -Wextra -g -O3 -std=c99 xx.c -o xx && ./xx
No Cast: 443118 = (3588+(10*(47541-3588)*1))
US Cast:  49902 = (3588+(10*(47541-3588)*1))
UL Cast: 443118 = (3588+(10*(47541-3588)*1))
$

我认为第二个几乎不可读的表达式准确反映(或足够准确地反映)16位编译器评估表达式的方式 - 并且与您所看到的一致。

(47541-3588)的结果是43953.(10 * 43953)%65536的结果是46314.添加3588,结果是,应该是49902。

我还向(unsigned long)添加了yk广告并运行了表达式。也许为了使用32位unsigned long的机器完全保真,我应该使用unsigned int,但结果不会改变。我不知道你从哪里获得了替代价值 - 我需要看到你的完整工作计划(类似于我的)以获得任何想法。看起来好像你有一部分计算'去负',给你留下大的(正)无符号值,但没有明显的借口使计算变为负数。


从评论中获取代码:

#include <stdio.h>
// -unused- #include "uart1.h"
// -unused- #include <stdlib.h>
// -unused- #include <stdint.h>
// -unused- #include <string.h>
// -unused- #include <math.h>

// -unused- int putchar(int c) { return uart1_putchar(c); }

int main(void)
{
    //variables
    unsigned short int xk=3588, yk=47541, yk1, sig=10, de=1;
    unsigned long xk1;
    // -not-needed-in-demo uart1_init();
    xk1=( xk+(sig*((unsigned long)yk-xk)*de));
    yk1=xk1 % 65535;
    //printf("xk1=%6lx\t\n,",xk1);
    //printf("yk1=%u\t\n,",yk1);
    printf("xk1 = %6lx = %6u\n", xk1, xk1);
    printf("yk1 = %6x = %6u\n", yk1, yk1);
}

65535应该是65536.该行末尾的标签是不必要的,下一个开头的逗号也是如此(但这些是纯化妆品)。

更严重(但由于未使用而对手头的问题无关紧要),<stdio.h>定义了一个名为putchar()的函数(通常也是一个宏)。您可能不应该定义自己的函数putchar,但如果必须,您通常应该从<stdio.h>取消定义宏(假设有一个)。我承认在我的机器上编译好的代码 - 没有链接,但这是预期的;也许有一天,我会追踪putchar()在这台机器上的真实情况。

显示的代码会生成正确/预期的答案。

我能看到产生观察到的不正确行为的唯一方法就是这样,我删除了多余的代码:

#include <stdio.h>

int main(void)
{
    unsigned short int xk=3588, yk=47541, yk1, sig=10, de=1;
    unsigned long xk1;
    xk1=( xk+(sig*((unsigned long)yk-xk)*de));
    yk1=xk1 % 65536;
    printf("xk1= %6lx = %6lu\n", xk1, xk1);
    printf("yk1= %6x = %6u\n", yk1, yk1);

    xk1=( xk+(sig*((short)yk-xk)*de));
    yk1=xk1 % 65536;
    printf("xk1= %6lx = %6lu\n", xk1, xk1);
    printf("yk1= %6x = %6u\n", yk1, yk1);
}

在我的64位计算机上运行(目前,MacOS X 10.6.7和GCC 4.6.0),我得到:

xk1=  6c2ee = 443118
yk1=   c2ee =  49902
xk1= fffffffffffcc2ee = 18446744073709339374
yk1=   c2ee =  49902

忽略十六进制值中的8个额外F,我得到0xFFF C C2EE而不是你得到的0xFFF B C2EE。我没有解释这种差异。但是你可以看到,如果中间结果是带符号的16位数量,那么你最终可能会得到你所看到的结果。

然后问题是:为什么那里有签名操作?我没有很好的解释。我想你可能需要查看汇编代码并找出正在发生的事情;你甚至可能在编译器中发现一个错误。