【问题标题】:Working with double-precision numbers in inline assembly (GCC, IA-32)在内联汇编中使用双精度数字 (GCC, IA-32)
【发布时间】:2010-02-12 05:47:32
【问题描述】:

我刚开始在我的计算机科学课上学习汇编,我有一个任务是使用指定的舍入模式对浮点值进行舍入。我尝试使用fstcw、fldcw 和frndint 来实现这一点。我修改了舍入控制位,将数字四舍五入,然后恢复之前的控制位(赋值的要求)。

当前突出的问题是指令fld %1似乎将错误的值加载到st(0)浮点寄存器中(例如,如果我调用值为2.6207的函数,则数字-1.9427(。 ..)e-29 被加载到寄存器中)。这可能是由于滥用了gcc 的内联asm() 或其他原因,但我不确定为什么会发生这种情况。

这是我所拥有的:

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 输入/输出相关的任何指针,我将不胜感激。 (当然,如果您发现其他问题,也请随时告诉我。)我不希望任何人为我做功课,只要指出我正确的方向即可。谢谢!

【问题讨论】:

  • 你能告诉我们这个函数的汇编输出吗?如果不是太麻烦的话,还有代码字节。
  • @John:将要组装的代码并不奇怪;这一切都在一个巨大的不透明(对 gcc)asm 块中。 :-P 然而,将其拆分为许多小的 asm 语句(如我的帖子)会给 gcc 更多的空间来做不同的事情。
  • 不清楚究竟将使用哪些代码字节,因为寻址模式似乎都是隐含的,而不是明确说明的。这个问题很可能是寻址问题而不是指令选择问题。

标签: gcc assembly x86


【解决方案1】:

您当前代码的至少一个问题是它使用了单精度浮点版本的 fld 和 fstp。如果你用 fldl 和 fstpl 替换它们,它可能会起作用。

【讨论】:

  • 我查过了。这似乎是正确的解决方案,因为 gcc 使用 AT&T 指令并要求 l 操作数,因此 FLDL 是正确的。
  • +1 是的,*l 版本似乎可以工作。我贴的sn-p在反汇编的时候也有*l后缀。 (显然,就我而言,我只是选择让 gcc 完成所有艰苦的工作,通过使用约束而不是在加载/存储指令中手动编码。)
  • 我试图保持问题的精神,但你的回答要好得多:)
【解决方案2】:

这就是我所拥有的。它没有经过测试,但希望对你来说不会那么粗糙。 :-)

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> 中的函数。 :-)

【讨论】:

  • 我需要使用汇编 :)
  • @jtbandes:酷。在这种情况下,请随时测试我的版本并让我知道需要修复的内容。 :-)
  • +t 约束如何工作?我在找到其他人的地方找不到有关它的信息。
  • @jtbandes:+ 表示输入/输出(用于指令读取和写入同一寄存器时使用),t 表示st(0)。阅读 GCC 手册的“机器约束”部分:gcc.gnu.org/onlinedocs/gccint/Machine-Constraints.html :-)
【解决方案3】:

随着符号的变化,这意味着符号位(最高位,第一个)不正确。 这对我来说假设指针 %1 对齐错误。如果你有一个字节,它可以 从 0,1,2... 开始,但如果您访问两个字节,地址必须是 0,2,4.... 以防万一 双倍地址必须能被 8 整除:0,8,16

所以检查你用来加载值的地址是否可以被 8 整除。Assembly 有 align 关键字来保证你的数据正确对齐。

【讨论】:

  • x86 不需要这种对齐方式,但仅出于性能考虑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-20
  • 1970-01-01
  • 2011-12-20
  • 2023-03-14
  • 1970-01-01
  • 2016-03-31
相关资源
最近更新 更多