如何在ARM中进行浮点计算?

Posted

技术标签:

【中文标题】如何在ARM中进行浮点计算?【英文标题】:How to do floating point calculation in ARM? 【发布时间】:2021-09-24 07:17:21 【问题描述】:

我正在尝试将两个十进制数字相加,例如 1.51.75。我把它们变成了十六进制的0x3fc000000x3fe00000。我尝试使用ADD 指令进行计算,但我得到了0x7fa00000,我认为正确的答案应该是0x40500000。有没有办法解决这个问题?谢谢!

【问题讨论】:

浮点数通过 NEON 扩展和额外的寄存器集来处理。另外还有vadd指令developer.arm.com/documentation/dui0473/m/neon-instructions/… @vadd NEON 对我不可用。我正在为 LPC2138 编程。 @XYWnot 如果您的 CPU 没有浮点指令(这是一个 ARM7TDMI-S,所以它没有),您必须自己实现这些浮点运算或调用合适的浮点点库。 @XYWnot 这是一个复杂的过程。请参阅像 Muller 等人的标准文本。 浮点算术手册了解详情。如果您对此不熟悉,最好使用浮点库而不是自己实现它。 最好使用软浮点库。只需添加两个非 nan 数字,这很容易,您可以自己完成。***有显然你已经知道的浮点格式,所以你应该能够自己做到这一点。一点小学数学(排列小数点,做加法,如果需要,四舍五入,标准化浮点数,完成)。先在 C 中尝试,然后在 asm 之后... 【参考方案1】:

你是正确的 0x3FC00000 和 0x3FE00000 结果是 0x40500000

您不能将定点加法直接与浮点数一起使用。正如您在 Wikipedia 或其他任何地方所看到的,单精度浮点数格式非常简单。您需要知道的大部分内容都是在小学时学到的。

是加法还是减法?在这种情况下添加,还好最简单。排列小数点(在本例中为二进制)。 做加法。然后浮点部分根据需要进行四舍五入并归一化。

单精度是符号位、指数和隐含 1.fraction 的分数。指数是做数学运算的浮动部分,你需要将较小的数字小数位转移到以太中,直到点排成一行。然后您可以使用定点加法。我为规范化做了一个循环,但实际上是为了加法,你不能溢出超过一位(例如 0x3+0x3 ​​= 0x6),所以正常数字的唯一规范化(不会上溢或下溢或不是nan 开始)是将其从 1x.fraction 转换为 1.fraction 或者它已经是 1.fraction 形式(用于添加两个正数)。

这里似乎有很多代码,但如果您需要做的只是添加两个 你知道的正常正数会产生一个正常的数字,你可以采取一些捷径。比我有的多。如果你不关心四舍五入,你可以采取更多。

但浮点数的加、减、乘、除并不复杂,因为您可以使用定点运算来完成任务(就像逻辑一样),您只需要准备操作数并规范化结果即可。

//float fun1 ( void )
//
//  return(1.5);
//
//float fun2 ( void )
//
//  return(1.75);
//
//float fun3 ( void )
//
//  return(1.75+1.5);
//
//
//Disassembly of section .text:
//
//00000000 <fun1>:
//   0: e3a005ff    mov r0, #1069547520 ; 0x3fc00000
//   4: e12fff1e    bx  lr
//
//00000008 <fun2>:
//   8: e59f0000    ldr r0, [pc]    ; 10 <fun2+0x8>
//   c: e12fff1e    bx  lr
//  10: 3fe00000    .word   0x3fe00000
//
//00000014 <fun3>:
//  14: e59f0000    ldr r0, [pc]    ; 1c <fun3+0x8>
//  18: e12fff1e    bx  lr
//  1c: 40400000    .word   0x40500000

#include <stdio.h>
int main ( void )

    unsigned int a,b;
    unsigned int ea,eb;
    unsigned int sa,sb;
    unsigned int x,y,z;
    unsigned int sxy;
    a = 0x3FC00000;
    b = 0x3FE00000;
    
    //shortcut just do positive numbers
    if(a&(1<<31)) return(1);
    if(b&(1<<31)) return(1);
    
    //exponents
    sa=(a>>23)&0xFF;
    sb=(a>>23)&0xFF;
    //line up the decimal places
    if(sa>sb)
    
        x=a&0x007FFFFF;
        x|=0x00800000;
        x<<=1; //room for rounding if desired
        y=b&0x007FFFFF;
        y|=0x00800000;
        y<<=1;
        while(sa!=sb)
        
            y>>=1;
            sb++;
        
        sxy=sa;
    
    else
    
        x=a&0x007FFFFF;
        x|=0x00800000;
        x<<=1;
        y=b&0x007FFFFF;
        y|=0x00800000;
        y<<=1;
        while(sa!=sb)
        
            y>>=1;
            sa++;
        
        sxy=sb;
    
    z=x+y;
    z++; //round up
    while(z&0xFE000000) //should just be if(0x02000000)
    
        z>>=1;
        sxy++;
    
    z>>=1; //remove sticky bit
    z&=0x007FFFFF;
    z|=sxy<<23;
    printf("0x%08X\n",z);
    
    return(0);

在开始之前了解这两个数字,我们可以作弊并采取一些捷径,而不必费心四舍五入。

#include <stdio.h>
int main ( void )

    unsigned int a,b;
    unsigned int ea,eb;
    unsigned int sa,sb;
    unsigned int x,y,z;
    unsigned int sxy;
    a = 0x3FC00000;
    b = 0x3FE00000;
    
    //shortcut already know they are positive numbers
    //exponents I already know are the same
    sxy=(a>>23)&0xFF;
    //line up the decimal places (already aligned)
    x=a&0x007FFFFF;
    x|=0x00800000;
    y=b&0x007FFFFF;
    y|=0x00800000;
    z=x+y;
    if(z&0x02000000)
    
        z>>=1;
        sxy++;
    
    z&=0x007FFFFF;
    z|=sxy<<23;
    printf("0x%08X\n",z);
    
    return(0);

在 asm 中编写代码并不难。

【讨论】:

以上是关于如何在ARM中进行浮点计算?的主要内容,如果未能解决你的问题,请参考以下文章

如何保持浮点/双精度算术确定性?

如何访问 NEON 指令中的完整 128 位?

如何通过频率来计算cpu的浮点运算能力滴?

ARM NEON 汇编和浮点舍入

揭秘ARM FPU 加速浮点计算

带有溢出的 ARM Neon 浮点整数转换的行为