Работа с числами двойной точности в линейной сборке (GCC, IA-32) - PullRequest
5 голосов
/ 12 февраля 2010

Я только начинаю изучать ассемблер в своем классе информатики, и у меня есть задание округлять значение с плавающей запятой, используя указанный режим округления. Я пытался реализовать это, используя fstcw, fldcw и frndint. Я изменяю контрольные биты округления, округляем число, а затем восстанавливаю предыдущие контрольные биты (требование назначения).

В настоящее время нерешенной проблемой является то, что инструкция fld %1, похоже, загружает неправильное значение в регистр с плавающей запятой st(0) (например, если я вызываю функцию со значением 2,6207, число -1,9427 (. ..) e-29 загружается в реестр). Это может быть связано с неправильным использованием asm() inline *1009* или с чем-то еще, но я не уверен, почему это происходит.

Вот что у меня есть:

double roundD (double n, RoundingMode roundingMode)
{
    // control word storage (2 bytes for previous, 2 for current)
    char *cw = malloc(4*sizeof(char));
    char *cw2 = cw + 2;

    asm("fstcw %3;" // store control word in cw
        "mov %3,%4;" // copy control word into cw2
        "and $0xF3FF,%4;" // zero out rounding control bits
        "or %2,%4;" // put new mode into rounding control bits
        "fldcw %5;" // load the modified control word
        "fld %1;" // load n into st(0)
        "frndint;" // round n
        "fstp %0;" // load st(0) back into n
        "fldcw %3;" // load the old control word from cw
        : "=m" (n)
        : "m" (n), "m" (roundingMode),
          "m" (cw), "r" (cw2), "m" (cw2) // mov requires one argument in a register
        );

    free(cw);

    return n;
}

Буду признателен за любые указания на то, что не так с этим кодом, особенно в отношении строки fld %1 и входов / выходов asm. (Конечно, если вы можете найти другие проблемы, не стесняйтесь, дайте мне знать о них.) Я не хочу, чтобы кто-то делал мою домашнюю работу для меня, просто укажите мне правильное направление. Спасибо!

Ответы [ 3 ]

2 голосов
/ 12 февраля 2010

По крайней мере одна проблема с вашим текущим кодом заключается в том, что он использует версии fld и fstp с плавающей запятой одинарной точности. Если вы замените их на fldl и fstpl, это, вероятно, сработает.

2 голосов
/ 12 февраля 2010

Вот что у меня есть. Это не проверено, но, надеюсь, вам будет не так легко работать. : -)

double
roundd(double n, short mode)
{
    short cw, newcw;

    __asm__("fstcw %w0" : "=m" (cw));
    newcw = cw & 0xf3ff | mode;
    __asm__("fldcw %w0" : : "m" (newcw));
    __asm__("frndint" : "+t" (n));
    __asm__("fldcw %w0" : : "m" (cw));
    return n;
}

Хотя, если вам не требуется использовать сборку для достижения режима округления, подумайте об использовании функций из <fenv.h>. : -)

0 голосов
/ 12 февраля 2010

При изменении знака это означает, что бит знака (который является самым старшим, первым) неверен. Это предполагает, что указатель% 1 неправильно выровнен. Если у вас есть один байт, он может начинаться с 0,1,2 ... но если вы обращаетесь к двум байтам, адрес должен быть 0,2,4 .... и в случае двойного адреса должен быть даже делится на 8: 0,8,16

Поэтому проверьте, можно ли разделить адрес, который вы используете для загрузки значения, на 8. Сборка имеет ключевое слово align, чтобы гарантировать правильное выравнивание ваших данных.

...