【问题标题】:Split Float into Digits and convert to ASCII including comma将浮点数拆分为数字并转换为包括逗号在内的 ASCII
【发布时间】:2020-12-07 06:01:57
【问题描述】:

我有一个 MCU,它向 LCD 发送一系列数字以显示。我需要发送他们的 ASCII 对应值才能使其正常工作。这些数字是浮点类型,小数部分的精度不超过 2 位。

问题是我需要一个函数来解析浮点数,获取其数字而不反转它们,将它们转换为 ASCII,并将逗号的 ASCII 代码放在数字中的位置。

例子:

数字 = 123.45

result = 49 50 51 44 52 53 //其中44是逗号的ASCII码

对编写一个可以做到这一点的函数有什么建议吗? 如果结果变量一次只保留一个 ASCII 代码,那就太棒了,因为我需要一次为每个符号调用 WriteToLCD 函数。

另外,我真的无法访问 sprintf、itoa、ftoa 等库函数。

一些代码示例:

void LCD_print(int number)
{
        char aux;
        int naux=0,maux=0;
        while(number!=0)
        {
            maux=number%10;
            naux=naux*10+maux;
            number=number/10;
        }
        while(naux)
        {
        aux=naux%10+'0';
        WriteToLCD(aux);
        naux=naux/10;
        }
}

我从互联网上的某个地方得到了这个函数,它在将整数转换为 ASCII 方面做得很好,但我需要将参数变成浮点数并将点的 ASCII 代码添加到结果中。

Main 看起来像这样:

void main()
{
//other stuff
float number = ReadValueFromSensor();
LCD_print(number);
}

【问题讨论】:

  • "这些数字是浮点类型,小数部分的精度不超过 2 位。" -- 该浮点数是以IEEE 754 格式之一还是其​​他格式存储的?
  • 一个小点... 46(十进制)是.的ASCII码[不是 44是,]。
  • 当我说不超过2位精度时,我的意思是我不需要向LCD发送超过2位,其余的可以忽略。想也许它可以帮助你们的算法编辑:谢谢克雷格,我会编辑这篇文章
  • 你能显示一些代码吗?至少函数声明? 在代码中如何使用它? number = 123.45 C 中的数字表示是怎样的?它是char[] 数组还是float 变量(或_Accum 变量:)?你用的是什么单片机?你有硬件浮动支持吗? I dont really have access to library functions like sprintf 为什么?作为一般建议,如果您的 MCU 不支持硬件浮点数,请考虑不使用浮点数 - 而是坚持使用整数。 Any suggestions for writing a function that can do this? 太宽泛了。你到底有什么问题?
  • 如果您只需要小数点后两位精度,您可以将浮点值乘以 100,然后将其转换为 C 中的 int。这样,您将获得一个整数这比期望值高 100 倍。之后您必须做的就是打印各个数字并将小数点插入正确的位置。但是,这将导致向零舍入,这可能是不希望的。

标签: c


【解决方案1】:

需要一个函数来解析浮点数,获取其数字而不反转它们,将它们转换为 ASCII,并将逗号的 ASCII 代码放在数字中的位置。

将值缩放 100.0 以仅适用于整数值,并在制作产品时提供 double 的额外精度。

使用类似整数的代码,其中首先使用fmod() 找到最低有效位,然后是下一个最高位,依此类推,将结果保存在最坏情况大小的缓冲区中。

最后以相反的顺序打印。

NaN 和无穷大需要额外的代码。

#include <assert.h>
#include <float.h>
#include <stdio.h>

//                    -     up to 38 digits      .  00  \0
#define PRINT_FLT_SZ (1 + (FLT_MAX_10_EXP + 1) + 1 + 2 + 1)

void print_flt(float f) {
  assert(isfinite(f));
  double x = fabs(round(f * 100.0));
  char buf[PRINT_FLT_SZ];
  char *p = buf + sizeof buf - 1;
  *p = '\0';
  int digit_count = 0;

  do {
    double digit = fmod(x, 10.0);
    x = (x - digit)/10;
    *(--p) = (char)(digit + '0');
    digit_count++;
    if (digit_count == 2) {
      *(--p) = '.';
    }
  } while (x || digit_count <= 2);

  if (signbit(f)) {
    *(--p) = ',';  // or '.'
  }

  while (*p) {
    // WriteToLCD(*p);
    putchar(*p);
    p++;
  }
  putchar('\n');
}

用法

int main() {
  print_flt(123.45f);     // OP's test
  print_flt(123.459f);    // round test
  print_flt(-FLT_MAX);    // range test
  print_flt(0.00500001f); // round test
  print_flt(-0.0);        // signed zero test
  //print_flt(-0.0/0.0); // TBD code for NaN and INF
}

输出

123,45
123.46
-340282346638528886604286022844204804240,00
0,01
-0,00
  

对于大于 1014 的值,x = (x - digit)/10; 可能存在一些额外的问题,而不是需要进行更多分析,但需要启动 OP。

【讨论】:

  • 我已经测试了你的代码,虽然它看起来有点复杂(对我来说),但效果很好。我进行了一些修改以返回 ASCII 值,它完全符合我的需要。我也感谢您针对个别情况采取了预防措施。
  • @DragonBase 对于更大的问题,我会使用仅基于整数的解决方案,而无需任何float。对我来说,使用float 会使事情复杂化。各有各的。
【解决方案2】:

最简单的解决方案,无需使用任何标准函数或库,也无需逆序打印数组。只使用简单的数学和循环,忽略精度错误:

void WriteFloatToLCD(float x) {
    // Prints minus sign if number is negative
    // Then converts it to positive for normal processing below
    if (x < 0) {
        x *= -1;
        WriteToLCD('-');
    }

    // Truncate float to int (gets the whole part)
    int whole = (int)x;

    // Difference between whole part and x is the decimal part, truncated
    int decimal = (int)(x * 100 - whole * 100);

    // "div" will be 10^(number of digits from the whole part) / 10
    int div = 1;
    while (div <= whole / 10) {
        div *= 10;
    }

    // Convert and print each digit from the whole part
    while (div > 0) {
        WriteToLCD((whole / div) + '0');
        whole %= div;
        div /= 10;
    }

    // Print the decimal separator
    WriteToLCD('.');

    // Convert and print each digit from the decimal part
    for (div = 10; div > 0; div /= 10) {
        WriteToLCD((decimal / div) + '0');
        decimal %= div;
    }
}

void main(void) {
    float number = ReadValueFromSensor();
    WriteFloatToLCD(number);
}

您可以通过比较 x != x 并在必要时打印“NaN”来实现对 NaN 的支持。如果您想支持更好的浮动精度或如果 x &gt; INT_MAX/100 如 cmets 中所述,则应进行进一步修改。

【讨论】:

  • 需要额外的代码来处理x &lt; 0x &gt; INT_MAX/100
  • @chux-ReinstateMonica 刚刚添加了对负数的支持。另一种情况我什至认为对 OP 来说是不必要的。
  • 带有舍入而非截断的替代代码:whole = x * 100 + 0.5; decimal = whole %100; whole /= 100;
  • True OP 向文本询问float,但没有使用float 值的子范围的详细信息。对我来说,我喜欢尽量减少排除的代码。
  • 感谢您回答 Toribio,我也测试了您的功能,虽然它做了它应该做的事情,但似乎 Monica 的有点坚固。尽管如此,你的努力还是值得赞赏的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多