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

How do i find the average of a floating point array in MASM?

我明天要交作业,我需要计算数组中浮点值的平均值。我似乎无法在书中或我的笔记中找到任何关于将整数转换为浮点数的相对有用的东西(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

怎么样:

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 寄存器。这些是 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