如何在 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)读入五个浮点数?”