对于这个编译器,整数的大小必须是 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-bit 机器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 位机器上运行时(目前是带有 GCC 4.6.0 的 MacOS X 10.6.7),我得到:
xk1= 6c2ee = 443118
yk1= c2ee = 49902
xk1= fffffffffffcc2ee = 18446744073709339374
yk1= c2ee = 49902
忽略十六进制值中的 8 个额外 F,我得到 0xFFF C C2EE 而不是你得到的 0xFFF B C2EE。我无法解释这种差异。但是您可以看到,如果中间结果是带符号的 16 位数量,那么您最终会得到几乎您所看到的结果。
那么问题来了:为什么里面有一个签名操作呢?我没有很好的解释。我认为您可能必须查看汇编代码并弄清楚发生了什么;你甚至可能会在编译器中发现一个错误。