assembly - 如何在 x86 汇编中划分 float ?

标签 assembly x86 floating-point sse x87

当我尝试编写 Heron 算法来计算 ECX 寄存器中的 sqrt 时,它不起作用。看起来问题是浮点数的除法,因为结果是整数。

我的算法:

 sqrtecx:

MOV EDX, 10 ; loop count
MOV EAX, 5 ; x_0 in heron algorythm
MOV DWORD[EBP-100], ECX  ; save INPUT (ecx is input)    
MOV DWORD[EBP-104], EDX  ; save loop count
jmp     loop
MOV     ECX, EAX ; move  OUTPUT to ECX

loop:

MOV DWORD[EBP-104], EDX ; save loop count
xor edx, edx

MOV ECX, EAX
MOV     EAX, DWORD[EBP-100]
DIV ECX
ADD EAX, ECX
XOR EDX, EDX
mov ecx, 2
DIV ecx

MOV EDX, DWORD[EBP-104] ; load loop count
DEC EDX
JNZ loop

最佳答案

您需要使用浮点指令集来实现您的目标。您可能会觉得有用的一些说明是:

fild <int>  - loads and integer into st0 (not an immediate)
faddp       - adds st0 to st1, and pop from reg stack (i.e. result in st0)
fdivp       - divides st1 by st0, then pop from reg stack (again, push the result in st0)

这是一个简短的示例片段(VS2010 内联汇编):
int main(void)
{
    float res;

    __asm {
        push    dword ptr 5;     // fild needs a memory location, the trick is
        fild    [esp];           // to use the stack as a temp. storage
        fild    [esp];           // now st0 and st1 both contain (float) 5
        add     esp, 4;          // better not screw up the stack
        fadd    st(0), st(0);    // st0 = st0 + st0 = 10
        fdivp   st(1), st(0);    // st0 = st1 / st0 = 5 / 10 = 0.5
        sub     esp, 4;          // again, let's make some room on the stack
        fstp    [esp];           // store the content of st0 into [esp]
        pop     eax;             // get 0.5 off the stack
        mov     res, eax;        // move it into res (main's local var)
        add     esp, 4;          // preserve the stack
    }

    printf("res is %f", res);    // write the result (0.5)
}

编辑:
正如 harold 指出的,还有一条直接计算平方根的指令,它是 fsqrt .操作数和结果都是 st0 .

编辑 #2:
我不确定你是否真的可以加载到 st0立即值作为我的 reference没有说明是否清楚。因此我做了一个小片段来检查,结果是:
    float res = 5.0 * 3 - 1;
000313BE D9 05 A8 57 03 00    fld         dword ptr [__real@41600000 (357A8h)]  
000313C4 D9 5D F8             fstp        dword ptr [res] 

这些是 357A8h 处的字节:
__real@41600000:
000357A8 00 00                add         byte ptr [eax],al  
000357AA 60                   pushad  
000357AB 41                   inc         ecx  

所以我必须得出结论,不幸的是,在加载和存储数字时,您必须将数字存储在主存储器中的某个位置。当然,使用我上面建议的堆栈不是强制性的,事实上你也可以在你的数据段或其他地方定义一些变量。

编辑 #3:
别担心,汇编是一个强大的野兽;) 关于你的代码:
mov     ecx, 169    ; the number with i wanna to root
sub     esp, 100    ; i move esp for free space
push    ecx         ; i save value of ecx
add     esp,4       ; push was move my ebp,then i must come back 
fld                 ; i load from esp, then i should load ecx 
fsqrt               ; i sqrt it
fst                 ; i save it on ebp+100 
add     esp,100     ; back esp to ebp

您缺少 fld 的操作数和 fst .看看你的评论,我想你想要 fld [esp]fst [esp] ,我不明白你为什么在谈论 ebp尽管。 ebp应该保存堆栈帧的开头(那里有很多我们不应该弄乱的东西)而 esp持有它的结束。我们基本上想在堆栈帧的末尾进行操作,因为在它之后只有没人关心的垃圾。
您还应该add esp, 4最后,在您计算并保存平方根之后。这是因为 push ecxsub esp, 4在引擎盖下为您推送的值腾出空间,并且在将值保存回来时仍然需要一些空间。正是为此,您还可以避免 sub esp, 100add esp, 100 , 因为房间已经由 push 为您准备好了.
最后一个“警告”:整数和浮点值以非常不同的方式表示,因此当您知道必须使用这两种类型时,请注意您选择的指令。您建议的代码使用 fldfst ,它们都对浮点值进行运算,因此您得到的结果不会是您期望的结果。一个例子? 00 00 00 A9 是 169 上的字节表示,但它表示浮点数 +2.3681944047089408e-0043(对于那些挑剔的人来说,它实际上是一个长双)。
所以,最终的代码是:
mov     ecx, 169;   // the number which we wanna root
push    ecx;        // save it on the stack
fild    [esp];      // load into st0 
fsqrt;              // find the square root
fistp   [esp];      // save it back on stack (as an integer)
// or fst [esp] for saving it as a float
pop ecx;            // get it back in ecx

关于assembly - 如何在 x86 汇编中划分 float ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8804770/

相关文章:

assembly - 汇编语言对 Fortran 程序员有帮助吗?

c# - 检测cpu架构的正确方法?

python-3.x - 如何在Python中将十六进制值转换为 float

math - tcl中的 float 比较

c - asm 关键字作为 C 中的标识符名称?

arrays - 如何将数组从程序集传递到 C 函数

c# - 为什么 Debug 构建中的 C# JIT 汇编代码中的每个方法中都有 cmp + je

c - 如何将无符号整数加载到 SIMD 中

x86 - CPU特权环: Why rings 1 and 2 aren't used?

c# - 如何将 NUnit 的 EqualTo().Within() 约束与自定义数据类型一起使用?