неожиданный результат в mspgcc - PullRequest
1 голос
/ 10 мая 2011

Я написал простой код на 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=443118 и yk1=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 Ответ

4 голосов
/ 10 мая 2011

Размер этого целого должен составлять 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-битной беззнаковой арифметике.


Эксперимент

Проводится на 64-битном RHEL5 (AMD x86 /64) Машина с GCC 4.1.2.Чтобы симулировать 16-разрядное целочисленное вычисление, я обильно добавил (вторую копию) выражение с приведением ((unsigned short)(...)).Двойное умножение получает только один бросок;результат не изменяется независимо от порядка, в котором выполняются два умножения (вдвойне нет, поскольку один из умножителей равен 1).И я включил (третью копию) выражение с (unsigned long) cast.

Тестовая программа:

#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-разрядную величину со знаком, вы можете получить в значительной степени тот результат, который видите.

Тогда возникает вопрос: почему там есть подписанная операция??У меня нет хорошего объяснения.Я думаю, что вам, возможно, придется взглянуть на ассемблерный код и понять, что происходит;Вы можете даже щекотать ошибку в компиляторе.

...