如何在C语言中将浮点数转换为4字节字符?

7
我希望将浮点数转换为4字节的字符数组,例如2.45。因此,2.45应该如下所示:'@' 'FS' 'Ì' 'Í',其二进制表示方法是2.45 = 01000000 00011100 11001100 11001101
我已经解决了这个问题,但复杂度比较高。你有什么好的想法吗?
感谢你的好回答。
请问您能告诉我如何从字符数组反向转换为浮点数吗?

2
char a[sizeof the_float]; memcpy(char_array, &the_float, sizeof the_float)怎么样? - user529758
2
“Ì”和“Í”仅代表一个字符集中的“11001100 11001101”。 - glglgl
3个回答

13

只需使用memcpy:

#include <string.h>

float f = 2.45f;
char a[sizeof(float)];

memcpy(a, &f, sizeof(float));
如果你需要相反的字节序,那么将 a 中的字节翻转是一件微不足道的事情,例如:

如果您需要相反的字节序,则可以在后面轻松地翻转 a 中的字节,例如:

int i, j;

for (i = 0, j = sizeof(float) - 1; i < j; ++i, --j)
{
    char temp = a[i];
    a[i] = a[j];
    a[j] = temp;
}

我该如何改变字节序? - 12oni
1
@12oin tmp = a[0]; a[0] = a[3]; a[3] = tmp; tmp=a[1]; a[1] = a[2]; a[2] = tmp; 用于改变字节序。 - nos

10

你有几种方法可以做到这一点,包括以下两种:

  1. 使用类型转换和指针:

  2. float f = 2.45;
    char *s = (char *) &f;
    

    请注意这种方法毫无安全可言,在“string”后没有字符串终止符。

  3. 使用union

  4. union u
    {
        float f;
        char s[sizeof float];
    };
    
    union u foo;
    foo.f = 2.45;
    

    现在可以访问 char 数组以获取字节值。还要注意,与第一种替代方案一样,没有字符串终止符。


“这实际上利用了未定义的行为” - 你能进一步解释一下吗? - Jimbo
2
memcpy()比你们两种方法都更受欢迎。(顺便说一下,只要别名指针是(有符号或无符号)char *,第一种方法就是“安全的”。而第二种方法也不会引发未定义行为。) - user529758
@Jimbo 切记,union 的所有成员共享同一内存。这意味着您只能可靠地获取最后设置的成员的值。如果我在示例中设定了 f 成员,则在设置另一个成员之前,应该仅获取到 f。否则,虽然不是未定义行为,但可能会导致意外的值。 - Some programmer dude
@H2CO3: “memcpy()是首选”…这是因为类型转换吗?如果不是,您能否进一步解释一下? - Jimbo
@Jimbo 是的。因为它总是有效的,不仅适用于 char。(因此更加防错。) - user529758
谢谢你的回答。你能告诉我如何从字符数组返回浮点数吗? - 12oni

0

可以使用类型转换和指针将浮点数转换为字符,方法如下:

float t= -2.63646464;
char *float2CharArr;
float2CharArr = (char*) &t;

请注意,上面没有\0字符串终止符。可以通过以下方式附加它:

char* tmp = realloc(float2CharArr, sizeof(*float2CharArr)+1);
tmp[sizeof(*float2CharArr)+1] = '\0';
float2CharArr=tmp;

另一种更详细的将浮点值转换为字符字符串的方法可以使用以下代码实现:

union int32_Float_t 
{
  int32_t Long;
  float Float;
};
 
#ifndef HUGE_VALF
#define HUGE_VALF (__builtin_huge_valf())
#endif
 
#ifndef FLT_MIN_EXP
#define FLT_MIN_EXP (-999)
#endif
 
#ifndef FLT_MAX_EXP
#define FLT_MAX_EXP (999)
#endif
 
#define _FTOA_TOO_LARGE -2  // |input| > 2147483520 
#define _FTOA_TOO_SMALL -1  // |input| < 0.0000001 
 
//precision 0-9
#define PRECISION 7
 
//_ftoa function 
void _ftoa(float f, char *p, int *status) 
{
  int32_t mantissa, int_part, frac_part;
  int16_t exp2;
  int32_Float_t x;
 
  *status = 0;
  if (f == 0.0) 
  {
    *p++ = '0';
    *p++ = '.';
    *p++ = '0';
    *p = 0;
    return;
  }
 
  x.Float = f;
  exp2 = (unsigned char)(x.Long>>23) - 127;
  mantissa = (x.Long&0xFFFFFF) | 0x800000;
  frac_part = 0;
  int_part = 0;
 
  if (exp2 >= 31) 
  {
    *status = _FTOA_TOO_LARGE;
    return;
  } 
  else if (exp2 < -23) 
  {
    *status = _FTOA_TOO_SMALL;
    return;
  } 
  else if (exp2 >= 23)
  { 
    int_part = mantissa<<(exp2 - 23);
  }
  else if (exp2 >= 0) 
  {
    int_part = mantissa>>(23 - exp2);
    frac_part = (mantissa<<(exp2 + 1))&0xFFFFFF;
  } 
  else 
  {
    //if (exp2 < 0)
    frac_part = (mantissa&0xFFFFFF)>>-(exp2 + 1);
  }
 
  if (x.Long < 0)
      *p++ = '-';
  if (int_part == 0)
    *p++ = '0';
  else 
  {
    ltoa(int_part, p, 10);
    while (*p)
      p++;
  }
  *p++ = '.';
  if (frac_part == 0)
    *p++ = '0';
  else 
  {
    char m;
 
    for (m=0; m<PRECISION; m++) 
    {
      //frac_part *= 10;
      frac_part = (frac_part<<3) + (frac_part<<1); 
      *p++ = (frac_part>>24) + '0';
      frac_part &= 0xFFFFFF;
    }
 
    //delete ending zeroes
    for (--p; p[0] == '0' && p[-1] != '.'; --p)
      ;
    ++p;
  }
  *p = 0;
}
 

以下是如何在Arduino Studio和相关的MCU编程平台上使用的示例:
void setup(void) 
{
  int i, stat;
  char s[20];
  float f[] = { 0.0, -0.0, 42.0, 123.456789, 0.0000018, 555555.555, 
                   -888888888.8888888, 11111111.2 };
                    
  Serial.begin(9600);
 
  for ( i=0; i<8; i++ ) 
  {
    if ( _ftoa(f[i], s, &stat) == 0 ) 
    {
      Serial.print( "_ftoa: " ); 
      Serial.println( s );
    }
  }
}
 
void loop(void) { }

这段源代码在互联网上的各个地方都有发布,大多数情况下没有署名。例如这里这里。因此要感谢匿名作者。


1
这样做不行。在C语言中,你不能给数组赋值。 - Steve Summit
你是正确的。现在已经被更正了。请注意,这样就没有 \0 字符串终止符了。 - Miguel Tomás
1
我猜测那些源代码可能是从某个C库(比如Newlib)中毫无保留地盗取且未注明来源。 - Lundin
我完全不知道,也没有时间去找。你能分享一个链接吗? - Miguel Tomás
1
我不知道从哪里来的,但是像 #define HUGE_VALF (__builtin_huge_valf()) 这样的东西闻起来像是直接从标准库中取出的。 - Lundin

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