首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >浮点数及其对8位微控制器内存的影响

浮点数及其对8位微控制器内存的影响
EN

Stack Overflow用户
提问于 2020-03-31 19:31:07
回答 3查看 1.3K关注 0票数 1

我目前正在从事一个项目,其中包括使用linux中的SDCC编译器在stm-8微控制器上进行裸金属编程。芯片中的内存很低,所以我试图让事情变得更精简。我用了8位和16位变量,一切都进行得很顺利。但最近我遇到了一个问题,我真的需要一个浮动变量。所以我写了一个函数,它接收一个16位的值,转换成一个浮点数,做我需要的数学运算,然后返回一个8位的数字。这使得我在MCU上的最后编译代码从1198字节上升到3462字节。现在,我知道使用浮点数是内存密集型的,可能需要调用许多函数来处理浮点数的使用,但是将程序的大小增加那么大似乎很疯狂。我想要一些帮助,了解这是为什么和发生了什么。

规格: MCU stm8151f2编译器: SDCC具有--opt_code_size选项

代码语言:javascript
复制
int roundNo(uint16_t bit_input) 
{ 
    float num = (((float)bit_input) - ADC_MIN)/124.0;
    return num < 0 ? num - 0.5 : num + 0.5; 
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-03-31 22:51:54

要确定为什么特定工具链上的代码如此之大,您需要查看生成的程序集代码,并查看它所调用的FP支持,然后查看映射文件以确定每个函数的大小。

例如,在哥德波特 for AVR上,使用GCC 5.4.0和-Os (Godbolt不支持STM8或SDCC,因此这是作为一个8位体系结构进行比较),您的代码生成6364字节,而空函数为4081字节。因此,代码体所需的附加代码为2283字节。现在,考虑到您使用的是不同的编译器和架构,它们与您的结果并没有太大的不同。请参阅生成的代码(下面)中的rcall到子程序(如__divsf3 )--这些是大部分代码所在的地方,我怀疑FP部门是最大的贡献者。

代码语言:javascript
复制
roundNo(unsigned int):
        push r12
        push r13
        push r14
        push r15
        mov r22,r24
        mov r23,r25
        ldi r24,0
        ldi r25,0
        rcall __floatunsisf
        ldi r18,0
        ldi r19,0
        ldi r20,0
        ldi r21,lo8(69)
        rcall __subsf3
        ldi r18,0
        ldi r19,0
        ldi r20,lo8(-8)
        ldi r21,lo8(66)
        rcall __divsf3
        mov r12,r22
        mov r13,r23
        mov r14,r24
        mov r15,r25
        ldi r18,0
        ldi r19,0
        ldi r20,0
        ldi r21,0
        rcall __ltsf2
        ldi r18,0
        ldi r19,0
        ldi r20,0
        ldi r21,lo8(63)
        sbrs r24,7
        rjmp .L6
        mov r25,r15
        mov r24,r14
        mov r23,r13
        mov r22,r12
        rcall __subsf3
        rjmp .L7
.L6:
        mov r25,r15
        mov r24,r14
        mov r23,r13
        mov r22,r12
        rcall __addsf3
.L7:
        rcall __fixsfsi
        mov r24,r22
        mov r25,r23
        pop r15
        pop r14
        pop r13
        pop r12
        ret

您需要对工具链生成的代码执行相同的分析,以回答您的问题。毫无疑问,SDCC能够生成一个程序集列表和一个映射文件,这将使您能够准确地确定生成和链接了哪些代码和FP支持。

最后,尽管在这种情况下完全没有必要使用FP:

代码语言:javascript
复制
int roundNo(uint16_t bit_input) 
{ 
  int s = (bit_input - ADC_MIN) ;
  s += s < 0 ? -62 : 62 ;
  return s / 124 ;
}

与空函数相比,在哥德波特 2283字节处。仍然有点大,但最有可能的问题是,AVR缺少DIV指令,因此称为__divmodhi4。STM8有一个16位红利和8位除数的DIV,因此它很可能会更小(更快)。

票数 2
EN

Stack Overflow用户

发布于 2020-03-31 21:28:22

好的,一个实际工作的不动点版本:

代码语言:javascript
复制
// Assume a 28.4 format for math.  12.4 can be used, but roundoff may occur.

// Input should be a literal float (Note that the multiply here will be handled by the  
// compiler and not generate FP asm code. 
#define TO_FIXED(x) (int)((x * 16))

// Takes a fixed and converts to an int - should turn into a right shift 4.
#define TO_INT(x)   (int)((x / 16))

typedef int FIXED;
const uint16_t ADC_MIN = 32768;

int roundNo(uint16_t bit_input) 
{ 
  FIXED num = (TO_FIXED(bit_input - ADC_MIN)) / 124;
  num += num < 0 ? TO_FIXED(-0.5) : TO_FIXED(0.5);
  return TO_INT(num);
}

int main()
{
  printf("%d", roundNo(0));

  return 0;
}

注意,我们在这里使用了一些32位的值,所以它将比当前的值更大。不过,小心点,如果可以小心地管理舍入和溢出,它可能会转换回12.4 (16位int)。

或者从web抓取一个更好的完整功能定点库:)

票数 0
EN

Stack Overflow用户

发布于 2020-04-01 08:55:04

(Update)写完这篇文章后,我注意到@Clifford提到您的微控制器本机支持这个DIV指令,在这种情况下,这样做是多余的。无论如何,我将把它保留为一个概念,它可以应用于DIV作为外部调用实现的情况,或者对于DIV周期过长、目标是使计算速度更快的情况。

无论如何,如果你需要挤出一些额外的周期,移动和加法可能比除法更快。因此,如果您从124几乎等于4096/33这一事实开始(误差因子为0.00098,即0.098%,小于千分之一),则可以使用33和移位12位(除以4096)实现除法。此外,3332+1,这意味着乘以33等于左移5并再次添加输入。

示例:您希望将5000除以124,而5000/124是接近的。40.323。我们要做的是:

  1. 5,000 << 5= 160,000
  2. 160 000+5 000=165 000
  3. 165,000 >> 12 = 40

注意,这只适用于正数。还注意到,如果您真的在代码中执行大量乘法操作,那么具有单个extern muldiv函数从长远来看可能会导致更小的整体代码,特别是在编译器不擅长优化的情况下。如果编译器可以在这里发出一个DIV指令,那么你唯一能得到的就是一点点的速度改进,所以不要为此烦恼。

代码语言:javascript
复制
#include <stdint.h>
#define ADC_MIN 2048

uint16_t roundNo(uint16_t bit_input) 
{ 
    // input too low, return zero
    if (bit_input < ADC_MIN)
        return 0;

    bit_input -= (ADC_MIN - 62);
    uint32_t x = bit_input;

    // this gets us x = x * 33        
    x <<= 5;
    x += bit_input;

    // this gets us x = x / 4096
    x >>= 12;

    return (uint16_t)x;
}

尺寸优化的GCC AVR产生,即所有对extern mul或div函数的调用都没有了,但似乎不支持在一条指令中移动多个位(它发射的循环分别移动5次和12次)。我不知道你的编译器会做什么。

如果您还需要处理bit_input < ADC_MIN案件,我将分别处理此部分,即:

代码语言:javascript
复制
#include <stdint.h>
#include <stdbool.h>
#define ADC_MIN 2048

int16_t roundNo(uint16_t bit_input) 
{ 
    // if subtraction would result in a negative value,
    // handle it properly
    bool negative = (bit_input < ADC_MIN);
    bit_input = negative ? (ADC_MIN - bit_input) : (bit_input - ADC_MIN);

    // we are always positive from this point on
    bit_input -= (ADC_MIN - 62);

    uint32_t x = bit_input;
    x <<= 5;
    x += bit_input;
    x >>= 12;

    return negative ? -(int16_t)x : (int16_t)x;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60958440

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档