将两个字符串合并成一个浮点数在stm32上?

4

我在stm32f3上通过UART接收到两个字符串值,想将它们连接成一个浮点数,但实际上我不知道如何做到这一点。让我们在下面的示例中进行操作。

char *s1="100";
char *s2="09"; //should result in 100.09

float x=???(s1,s2);

我该如何实现这个?

这些 字符串 是否像 char *s1="100"; char *s2="-09"; 或者 "-100"; "-09"; 一样是负数吗? - chux - Reinstate Monica
5个回答

3

我会将每个字符串转换为整数,然后使用算术运算:

int whole = atoi(s1);
int frac = atoi(s2);

float x = whole + frac / powf(10, strlen(s2));

最后一部分计算10^strlen(s2),因为"09"表示09/100,"5"表示5/10等。

如果您的系统上无法使用powf()(如您在评论中所说),则可以使用此方法(仅适用于小型非负输入,但这就是您拥有的):

float myexp10(unsigned x)
{
    if (x == 0)
        return 1.0;

    float res = 10;
    while (--x)
        res *= 10;

    return res;
}

1
或者 float x = whole + frac * powf(10, - (int)strlen(s2)); - Bob__
@HansPeterLoft:pow() 怎么样?在这里同样适用,我很确定它可以在 STM32 上工作。您可能需要通过添加编译器/链接器选项 -lm 来链接 libm,或者解释一下您所说的“不起作用”是什么意思。 - John Zwinck
@HansPeterLoft,如果powf无法使用,请实现它。 - CroCo
@HansPeterLoft:如果你的系统真的没有 pow(),我为你编写了一个替代函数。 - John Zwinck
如果你最终编写了一个自定义的 myexp10,那么你也可以编写一个自定义的 float my_decimal_part(const char *)... - Bob__

2

一种方法是将两个字符串打印到一个缓冲区中,然后使用 strtod 读取一个 float

char buf[10];
sprintf(buf, "%.4s.%.4s", s1, s2);
float f = strtod(buf, NULL);
printf("%f\n", f);

请注意,由于100.09无法精确表示为float,因此结果将接近于100.089996
演示: Demo.

strtod在stm32上不存在,sscanf似乎也无法正常工作。我的缓冲区看起来像这样:buf = {'1','0','0','.','0','9','\ n','\ 0','\ 0'....},val是一个浮点数。 然后我这样调用它:sscanf(buf,“%f”,&val); 但val变成了一些随机非常小的值。 - HansPeterLoft
@HansPeterLoft 很奇怪 strtod 不可用。我假设你已经包含了 stdlib.h,对于 buf 的内容,sscanf 应该可以工作,假设 val 是一个 float 而不是 double - Sergey Kalinichenko

1
我会将这两个字符串用点连接起来,然后使用strtof()提取浮点数,代码如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void) {
    char *s1 = "100";
    char *s2 = "09";
    char s[strlen(s1) + 1 + strlen(s2) + 1];
    strcpy(s, s1);
    strcat(s, ".");
    strcat(s, s2);
    float result = strtof (s, NULL);
    printf("%f\n", result);
    return 0;
}

输出:

100.089996

如果这种精度对您来说不够,那么可以使用strtod(),像这样:
double result = strtod (s, NULL);

0

另一种简单但繁琐的方法是将它们存储在字符数组中,并使用atof()进行转换,如下所示:

#include <stdio.h>
#include <string.h> /* strlen */
#include <stdlib.h> /* atof */


float str2float(const char* s1, const char* s2)
{
    int size = strlen(s1) + strlen(s2) + 2;
    char a[size];

    for(int i = 0; i < strlen(s1); ++i)
        a[i] = s1[i];
    a[strlen(s1)] = '.';

    for(int i = strlen(s1)+1, j = 0; i < size && j < strlen(s2); ++i, ++j)
        a[i] = s2[j];

    a[size] = '\0';

    return (float)atof(a);
}

int main(void)
{
    char *s1="100";
    char *s2="09"; 

    printf("%.2f\n", str2float(s1,s2));
    return 0;
}

0

考虑按照其他人的建议,对小数部分进行缩放并相加。

  char *s1="100";
  char *s2="09";
  float f1 = strtof(s1, (void*) NULL); 
  unsigned n = strlen(s2);
  float f2 = strtof(s2, (void*) NULL)/powf(10, n);
  return f1 + f2;

这通常已经足够了,但是要深入一点。

strtof(s2, (void*) NULL)/powf(10, n); 在除法运算中会产生舍入误差,因为商数(一个分数)很少能够被准确地表示为binary32。然后加法f1 + f2可能会再次产生舍入误差。由于双重舍入的原因,总和有时不是最好的float答案。

为了大大降低双重舍入的机会,代码可以轻松使用更高精度的double

  float f1 = strtof(s1, (void*) NULL); 
  unsigned n = strlen(s2);
  double f2 = strtod(s2, (void*) NULL)/pow(10, n); // use higher precision
  return f1 + f2;

或者代码可以避免使用更广泛的FP数学,首先使用精确的整数数学来组合值,然后进行除法运算,只产生1次舍入。这将使答案偶尔更准确。

  long l1 = strtol(s1, (void*) NULL, 10); 
  long l2 = strtol(s2, (void*) NULL, 10); 
  unsigned n = strlen(s2);
  long p10 = pow10(n);  // Trivial user TBD code

  long sum = l1*p10 + l2;  // Exact unless `long` overflow
  return 1.0f*sum/p10;     // `float` conversion exact for value up to about 1/FLT_EPSILON

如果s1负数,上述方法很有效。对于s2负数的情况也可以,但需要更多的代码来提取数字宽度,而不是使用strlen(s2)

否则,很难击败字符串连接和转换。我建议使用float strof()而不是double atof(),因为1)atof()在溢出时缺乏定义行为,2)atof()使用double数学运算,可能会更慢。

size_t l1 = strlen(s1);
size_t l2 = strlen(s1);
char buffer[l1 + 1 + l2 + 1];  // Or use a fixed wide buffer with test to insure fit
strcpy(buffer, s1);
buffer[l1] = '.';
strcpy(buffer + l1 + 1, s1);
return strtof(buffer, (void *) NULL);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接