如何在 MASM 中找到浮点数组的平均值?

Posted

技术标签:

【中文标题】如何在 MASM 中找到浮点数组的平均值?【英文标题】:How do i find the average of a floating point array in MASM? 【发布时间】:2015-11-12 23:33:23 【问题描述】:

我有一个明天到期的作业,我需要在一个数组中找到浮点值的平均值。我似乎在书中或我的笔记中找不到任何相对有用的关于将整数转换为浮点数(ecx(数组长度)中的值 5 为 5.0,因此我可以在不截断的情况下进行除法)。

这是给我的代码,只有两行标记为 line1 和 line2 需要更改,但我似乎无法弄清楚它们需要更改为什么。关于如何完成这项工作的任何想法?

c++ 文件

#include <stdio.h>
extern"C"

    float average(float [], int);   // external assembly function prototypes
    float max(float [], int);
    float min(float [], int);



int main()

    const int SIZE = 5;
    float floatArr[SIZE] = 2.2, 3.75, 1.11, 5.9, 4.64;

    printf("The array contains the float numbers: ");

    for (int i = 0; i<SIZE; i++)
        printf("%f ", floatArr[i]);

    float val1 = average(floatArr, SIZE);
    printf("\n\nThe average of the floats are: %f\n", val1);

    float val2 = max(floatArr, SIZE);
    printf("The largest float is: %f\n", val2);

    float val3 = min(floatArr, SIZE);
    printf("The smallest float is: %f\n", val3);

    return 0;

asm 文件

.686
.model flat

.code 

_average PROC

        push ebp                ; save the caller frame pointer
        mov ebp, esp

        mov ebx, [ebp+8]    ; address of first element in array
        mov ecx, [ebp+12]   ; store size of array in ecx
        xor edx, edx        ; counter for loop
        fldz            ; set top of FPU stack to zero

loopAdd:
        fld dword ptr[ebx+edx*4]   ; load next array onto register stack at st(1)
        faddp              ; add st(0) to st(1) and pop register stack
        inc edx            ; increment counter
        cmp ecx, edx           ; compare size of array in ecx with counter in edx
        jg loopAdd         ; if ecx > edx jump to loopAdd and continue

line1   cvtsi2sd eax, xmm0      ;load array size as float to compute average
line2   fdivp                 ;divide st(0) by st(1) and pop register stack


        pop ebp            ; restore caller frame pointer
        ret                ; content of st(0) is returned 

_average ENDP

END

【问题讨论】:

为什么要混合 x87 和 sse 指令? 作为 MASM 的新手,我认为这没有什么害处。我错了吗?你能花时间解释一下吗? 好吧,并不是不能完成,但是 xmm0 是一个与 st(0) 完全不同的寄存器,您似乎假设它们是相关的。要在 fpu 堆栈上加载整数,可以使用 fild 之类的东西。 我同意@harold 和使用FILD。不要使用 SSE(SIMD 指令)。如果您在大部分工作中使用 x87 FPU,请坚持使用。您的 MASM 汇编器可能不接受像 cvtsi2sd eax, xmm0 这样的 SIMD 指令的原因是因为它是旧版本,或者您需要 .XMM 指令以及 .686 好的,那么我将如何使用 FILD? “菲尔德 eax”? 【参考方案1】:

我决定查看您的代码以提出解决方案。不要使用 xmm 寄存器。这些是 SIMD 指令,由于您的其余代码使用 x87 FPU,我建议继续使用 x87 FPU 指令。

您的代码似乎正确地对数组中的所有数字求和,并将该总和留在寄存器 st(0) 中。您还可以在 ECX 中除以项目数。所以你需要将 st(0) 除以 ECX 中的整数值。

为此,您必须将 ECX 的值临时存储在临时内存变量中。这是因为FIDIV instruction 不接受寄存器操作数。 FIDIV 将做的是将 st(0)(FPU 堆栈的顶部)除以 32 位内存位置指定的 32 位整数。

您需要先在函数中添加一个.data 部分来保存整数值(numitems):

.data
numitems DWORD 0  
.code

而不是你在这里尝试的:

line1   cvtsi2sd eax, xmm0  ;load array size as float to compute average
line2   fdivp               ;divide st(0) by st(1) and pop register stack

这样做:

mov numitems, ecx           ;Move ecx(# of items in array) to numitems variable
FIDIV numitems              ;divide st(0) by value in numitems variable
                            ;After division st(0) should contain the average

代码如下所示:

.686
.model flat

.code 
_average PROC
        .data
        numitems DWORD 0  
        .code

        push ebp                ; save the caller frame pointer
        mov ebp, esp

        mov ebx, [ebp+8]    ; address of first element in array
        mov ecx, [ebp+12]   ; store size of array in ecx
        xor edx, edx        ; counter for loop
        fldz            ; set top of FPU stack to zero

loopAdd:
        fld dword ptr[ebx+edx*4]   ; load next array onto register stack at st(1)
        faddp              ; add st(0) to st(1) and pop register stack
        inc edx            ; increment counter
        cmp ecx, edx           ; compare size of array in ecx with counter in edx
        jg loopAdd         ; if ecx > edx jump to loopAdd and continue

        mov numitems, ecx  ;Move ecx(# of items in array) to numitems variable
        FIDIV numitems     ;divide st(0) by value in numitems variable
                           ;After division st(0) should contain the average

        pop ebp            ; restore caller frame pointer
        ret                ; content of st(0) is returned 

_average ENDP

END

此函数不可重入,因为它有效地使用静态变量 numitems 来临时存储 ECX 。可以通过将值临时放在堆栈上并执行FIDIV 来摆脱这个临时静态变量。其代码消除了 .data 部分,并使用当前堆栈指针正下方的 4 个字节足够长来执行 FIDIV,然后简单地丢弃整数值。

.686
.model flat

.code 
_average PROC
        push ebp                ; save the caller frame pointer
        mov ebp, esp

        mov ebx, [ebp+8]    ; address of first element in array
        mov ecx, [ebp+12]   ; store size of array in ecx
        xor edx, edx        ; counter for loop
        fldz            ; set top of FPU stack to zero

loopAdd:
        fld dword ptr[ebx+edx*4]   ; load next array onto register stack at st(1)
        faddp              ; add st(0) to st(1) and pop register stack
        inc edx            ; increment counter
        cmp ecx, edx           ; compare size of array in ecx with counter in edx
        jg loopAdd         ; if ecx > edx jump to loopAdd and continue

        mov [esp-4], ecx  ;Move ecx(# of items in array) to temp location on stack
        fidiv dword ptr [esp-4] 
                           ;divide st(0) by value in temporary stack location
                           ;After division st(0) should contain the average

        pop ebp            ; restore caller frame pointer
        ret                ; content of st(0) is returned 
_average ENDP
END

作为替代方案,由于 ECX 已在内存位置 EBP+12 的堆栈中传入,因此可以通过删除所有这些行来修改最后一个示例

    mov [esp-4], ecx  ;Move ecx(# of items in array) to temp location on stack
    fidiv dword ptr [esp-4] 
                       ;divide st(0) by value in temporary stack location
                       ;After division st(0) should contain the average

并用这一行替换它:

    fidiv dword ptr [ebp+12] 
                       ;divide st(0) by SIZE (2nd argument passed on stack)
                       ;After division st(0) should contain the average

【讨论】:

哇,非常感谢您抽出宝贵时间!我非常感谢您,因为您让我走上了正确理解 FPU 的道路,我发现自己兴奋地咯咯笑了起来。希望你有一个美好的夜晚! @drnips 很高兴它有帮助。如果答案确实有助于回答您的问题,您可以accept the answer 以便 *** 可以将其标记为已解决。如果您是 SO 新手,并且想知道如何以及为什么接受答案,请参阅以下信息:meta.stackexchange.com/a/5235/271768 @drnips:感谢您接受此答案,并感谢您接受不久前帮助您的其他人的答案。欢迎来到 SO!【参考方案2】:

怎么样:

cvtsi2sd xmm0, ecx
fdiv xmm0

看起来确实太简单了……

CVTSI2SD xmm, r/m32 Convert one signed doubleword integer from r/m32 to one double-precision floating-point value in xmm.

FDIV m32fp  Divide ST(0) by m32fp and store result in ST(0).

【讨论】:

使用 xmm 根本不起作用。我收到错误“错误 A2085:指令或寄存器”。我找不到任何有用的解决方法。 =/

以上是关于如何在 MASM 中找到浮点数组的平均值?的主要内容,如果未能解决你的问题,请参考以下文章

浮点格式数组的平均值

在浮点数组中找到最小值

Unity3d - 计算 10 个最近不断变化的浮点数的平均值

什么叫“写一个循环(for或者while)读入五个浮点数?”

字节数组byte[]和整型,浮点型数据的转换——Java代码

如何在java中将4字节数组转换为浮点数