【问题标题】:Convert float to string without sprintf()不使用 sprintf() 将浮点数转换为字符串
【发布时间】:2014-04-21 05:13:32
【问题描述】:

我正在为基于微控制器的应用程序进行编码,我需要将float 转换为字符串,但我不需要与 sprintf() 相关的大量开销。有什么雄辩的方法可以做到这一点吗?我不需要太多。我只需要2位数的精度。

【问题讨论】:

  • 我担心sprintf 是您唯一的选择,顺便说一句,我的主要工作是微控制器,我从来没有觉得有必要使用快捷方式sprintf!你能告诉我你的原因是什么(只是好奇)
  • @chouaib 我同意。原因是因为我在 IAR Embedded Workbench 中进行开发,并且我使用的是代码大小限制(学生)版本。所以 sprintf() 使我的代码膨胀到超过 4k 的限制。
  • @Rohan 不,不会。它与我想要的完全相反。我想从浮动到字符串。 strtof() 则相反:字符串 -> 浮动。
  • 那么我会考虑从头开始制作它,即使我不知道它会比sprintf 好多少,您的浮点数是否在合理范围内?
  • 自行车里程表?为什么你需要花车?

标签: c string memory floating-point printf


【解决方案1】:

这是一个针对嵌入式系统优化的版本,不需要任何 stdio 或 memset,并且内存占用少。您负责传递一个用零初始化的 char 缓冲区(使用指针 p)来存储您的字符串,并在创建所述缓冲区时定义 CHAR_BUFF_SIZE(因此返回的字符串将以空值终止)。

static char * _float_to_char(float x, char *p) {
    char *s = p + CHAR_BUFF_SIZE; // go to end of buffer
    uint16_t decimals;  // variable to store the decimals
    int units;  // variable to store the units (part to left of decimal place)
    if (x < 0) { // take care of negative numbers
        decimals = (int)(x * -100) % 100; // make 1000 for 3 decimals etc.
        units = (int)(-1 * x);
    } else { // positive numbers
        decimals = (int)(x * 100) % 100;
        units = (int)x;
    }

    *--s = (decimals % 10) + '0';
    decimals /= 10; // repeat for as many decimal places as you need
    *--s = (decimals % 10) + '0';
    *--s = '.';

    while (units > 0) {
        *--s = (units % 10) + '0';
        units /= 10;
    }
    if (x < 0) *--s = '-'; // unary minus sign for negative numbers
    return s;
}

在 ARM Cortex M0 和 M4 上测试。正确舍入。

【讨论】:

  • 注意:这会将0 转换为.00,如果你想要0.00,请改用do..while
  • 点后两位数 - 好的。我需要一个快速的解决方案,因为 printf() 不适用于我的 CubeIDE 项目,尽管我设置了 -u _printf_float 并且链接器脚本似乎没问题。您的功能适合我的需要,因为我只需要在调试控制台上进行粗略的浮点输出即可查看该值是否存在。太棒了,谢谢。
【解决方案2】:

试试这个。它应该又好又小。我直接输出了字符串 - 执行 printf,而不是 sprintf。我将留给您为返回字符串分配空间,并将结果复制到其中。

// prints a number with 2 digits following the decimal place
// creates the string backwards, before printing it character-by-character from
// the end to the start
//
// Usage: myPrintf(270.458)
//  Output: 270.45
void myPrintf(float fVal)
{
    char result[100];
    int dVal, dec, i;

    fVal += 0.005;   // added after a comment from Matt McNabb, see below.

    dVal = fVal;
    dec = (int)(fVal * 100) % 100;

    memset(result, 0, 100);
    result[0] = (dec % 10) + '0';
    result[1] = (dec / 10) + '0';
    result[2] = '.';

    i = 3;
    while (dVal > 0)
    {
        result[i] = (dVal % 10) + '0';
        dVal /= 10;
        i++;
    }

    for (i=strlen(result)-1; i>=0; i--)
        putc(result[i], stdout);
}

【讨论】:

  • 也许在开头加上0.005fVal;这样你就不会最终将 269.9999834 打印为 269.99 而不是 270.00
  • (int)(fVal * 100) 由于超出范围,int 转换很容易失败。然而,OP 似乎并不关心这一点。
  • 确实,您应该先将值与INT_MAX/100INT_MIN/100 进行比较...除非保证范围在范围内
【解决方案3】:
// convert float to string one decimal digit at a time
// assumes float is < 65536 and ARRAYSIZE is big enough
// problem: it truncates numbers at size without rounding
// str is a char array to hold the result, float is the number to convert
// size is the number of decimal digits you want


void FloatToStringNew(char *str, float f, char size)

{

char pos;  // position in string

    char len;  // length of decimal part of result

    char* curr;  // temp holder for next digit

    int value;  // decimal digit(s) to convert

    pos = 0;  // initialize pos, just to be sure

    value = (int)f;  // truncate the floating point number
    itoa(value,str);  // this is kinda dangerous depending on the length of str
    // now str array has the digits before the decimal

    if (f < 0 )  // handle negative numbers
    {
        f *= -1;
        value *= -1;
    }

     len = strlen(str);  // find out how big the integer part was
    pos = len;  // position the pointer to the end of the integer part
    str[pos++] = '.';  // add decimal point to string

    while(pos < (size + len + 1) )  // process remaining digits
    {
        f = f - (float)value;  // hack off the whole part of the number
        f *= 10;  // move next digit over
        value = (int)f;  // get next digit
        itoa(value, curr); // convert digit to string
        str[pos++] = *curr; // add digit to result string and increment pointer
    }
 }

【讨论】:

    【解决方案4】:

    当你们回答时,我想出了我自己的解决方案,它更适合我的应用程序,我想我会分享。它不会将浮点数转换为字符串,而是 8 位整数。我的数字范围非常小(0-15)并且总是非负数,所以这将允许我通过蓝牙将数据发送到我的安卓应用程序。

    //Assumes bytes* is at least 2-bytes long
    void floatToBytes(byte_t* bytes, float flt)
    {
      bytes[1] = (byte_t) flt;    //truncate whole numbers
      flt = (flt - bytes[1])*100; //remove whole part of flt and shift 2 places over
      bytes[0] = (byte_t) flt;    //truncate the fractional part from the new "whole" part
    }
    //Example: 144.2345 -> bytes[1] = 144; -> bytes[0] = 23
    

    【讨论】:

    • 注意:这会丢失部分“2 位精度”,如帖子中所述。简单修复:flt += 0.005 首先。
    • @chux 如果我想要 9 位精度该怎么办?
    • @user12321 快速回答是flt += 1e-9/2; ... flt = (flt - bytes[1])*1e9;,但整数数学溢出等其他问题可能会在其他地方发挥作用。最好在提供所需范围和预期输入/输出类型的详细信息后制定答案。
    【解决方案5】:

    enhzflep的回复我无法评论,但要正确处理负数(当前版本没有),你只需要添加

    if (fVal < 0) {
         putc('-', stdout);
         fVal = -fVal;
      }
    

    在函数的开头。

    【讨论】:

      【解决方案6】:

      它是一个 Liitle 大方法,但它适用于 int 和 float,decimalPoint 参数以零值传递 Integer,如果您有比这更小的函数,请告诉我。

      void floatToStr(uint8_t *out, float x,int decimalPoint)
      {
          uint16_t absval = fabs(x);
          uint16_t absvalcopy = absval;
      
      
          int decimalcount = 0;
      
          while(absvalcopy != 0)
          {
      
              absvalcopy /= 10;
              decimalcount ++;
          }
      
          uint8_t *absbuffer = malloc(sizeof(uint8_t) * (decimalcount + decimalPoint + 1));
          int absbufferindex = 0;
          absvalcopy = absval;
          uint8_t temp;
      
          int i = 0;
          for(i = decimalcount; i > 0; i--)
          {
              uint16_t frst1 = fabs((absvalcopy / pow(10.0, i-1)));
              temp = (frst1 % 10) + 0x30;
              *(absbuffer + absbufferindex) = temp;
              absbufferindex++;
          }
      
          if(decimalPoint > 0)
          {
              *(absbuffer + absbufferindex) = '.';
              absbufferindex ++;
      
              //------------------- Decimal Extractor ---------------------//
             for(i = 1; i < decimalPoint + 1; i++)
             {
      
                 uint32_t valueFloat = (x - (float)absval)*pow(10,i);
                 *(absbuffer + absbufferindex) = ((valueFloat) % 10) + 0x30;
                 absbufferindex++;
             }
          }
      
         for(i=0; i< (decimalcount + decimalPoint + 1); i++)
         {
             *(out + i) = *(absbuffer + i);
         }
      
         i=0;
         if(decimalPoint > 0)
             i = 1;
         *(out + decimalcount + decimalPoint + i) = 0;
      
      }
      

      【讨论】:

      • absbuffer 没有被释放
      猜你喜欢
      • 2011-11-25
      • 1970-01-01
      • 2014-09-25
      • 1970-01-01
      • 1970-01-01
      • 2020-09-14
      • 1970-01-01
      相关资源
      最近更新 更多