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

时间:2015-11-12 23:33:23

标签: arrays floating-point average 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

2 个答案:

答案 0 :(得分:2)

我决定查看您的代码以提出解决方案。不要使用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

答案 1 :(得分:0)

怎么样:

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).