使用C语言中的printf函数将IEEE 754浮点数转换为十六进制。

17

理想情况下,以下代码将接受IEEE 754表示的浮点数并将其转换为十六进制。

void convert() //gets the float input from user and turns it into hexadecimal
{
    float f;
    printf("Enter float: ");
    scanf("%f", &f);
    printf("hex is %x", f);
}

我不太确定出了什么问题。它正在将数字转换为十六进制数字,但是非常错误。

123.1443 gives 40000000
43.3     gives 60000000
8        gives 0

所以它正在执行某些操作,只是我不太确定具体是什么。

需要帮助,谢谢!


在可变参数函数中,float 会被提升为 double(请参考 James McNellis 的回答)。建议使用 printf("hex is %lx", f);。如果要编写可移植的代码,请比较 sizeof(double)sizeof(int) 以及 sizeof(long),然后决定是使用 x 还是 lx - tomlogic
你如何确定它是 IEEE 754? - Ciro Santilli OurBigBook.com
@tomlogic:对于可移植的代码,请不要将浮点值传递给需要整数的printf格式。即使大小恰好匹配,行为也是未定义的。 (来自未来7年的问候。别问我怎么知道的。) - Keith Thompson
8个回答

26
当你将一个 float 作为变参函数(如 printf())的参数传递时,它会被提升为 double 类型,而 double 的大小是 float 的两倍(至少在大多数平台上是这样)。
解决这个问题的一种方法是,在将其作为参数传递给 printf() 时将 float 强制转换为 unsigned int
printf("hex is %x", *(unsigned int*)&f);

这样做更加正确,因为printf()使用格式说明符确定每个参数的大小。

从技术上讲,这种解决方案违反了严格类型别名规则。您可以通过将float的字节复制到unsigned int中,然后将其传递给printf()来绕过此问题:

unsigned int ui;
memcpy(&ui, &f, sizeof (ui));

printf("hex is %x", ui);

这两种解决方案都基于一个假设:sizeof(int) == sizeof(float),这在许多32位系统上是成立的,但不一定总是成立的。


1
为避免违反严格别名规则,请尝试使用*(volatile unsigned int *)(volatile float *)(&f)。 - Joshua
为什么你不能只是使用 printf("hex is %x\n",(unsigned int)f) ?假设 f 可以适应 unsigned int。 - Nyan
4
因为这样不会重新解释比特位作为一个无符号值,而是将浮点数转换为无符号值(所以24.12会变成24,这不是期望的结果)。 - James McNellis
2
这个自动提升双倍成本让我浪费了很多很多小时。非常沮丧 :( - 0x64

12

如果支持,可以使用%a将浮点数转换为标准十六进制格式。这是我能找到的唯一列出%a选项的文档

否则,您必须将浮点值的位拆分成已知大小的整数类型。例如,如果您知道float和int都是32位,则可以快速进行强制转换:

printf( "%08X" , *(unsigned int*)&aFloat );

如果你想减少对大小的依赖,可以使用一个联合体:

union {
  float f;
//char c[16]; // make this large enough for any floating point value
  char c[sizeof(float)]; // Edit: changed to this
} u;

u.f = aFloat;
for ( i = 0 ; i < sizeof(float) ; ++i ) printf( "%02X" , u.c[i] & 0x00FF );

循环的顺序取决于架构字节序。此示例为大端字节序。

无论如何,浮点格式可能无法在其他架构上移植。%a选项旨在实现可移植性。


+1:我之前不熟悉%a格式说明符,现在感觉有点傻。 - James McNellis
+1 是因为了解 %a。为什么要使用“char[16]”而不是“char[sizeof(float)]”? - Jonathan Leffler
C99是另一个提到它的文档 :-) - Ciro Santilli OurBigBook.com

4

十六进制转浮点数

我曾经花了很长时间试图弄清楚如何将从串行连接中以IEE754浮点格式格式化的十六进制输入转换为浮点数。现在我成功了,只是想分享一下,以防有助于其他人。

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{

    uint16_t tab_reg[64]                    //declare input value recieved from serial connection
    union IntFloat {  int32_t i;  float f;  }; //Declare combined datatype for HEX to FLOAT conversion
    union IntFloat val;                     
    int i;
    char buff[50];                          //Declare buffer for string

    i=0;

    //tab_reg[i]=0x508C;                    //to test the code without a data stream,   
    //tab_reg[i+1]=0x4369;                  //you may uncomment these two lines.


    printf("Raw1: %X\n",tab_reg[i]);        //Print raw input values for debug
    printf("Raw2: %X\n",tab_reg[i+1]);      //Print raw input values for debug

    rs = sprintf(buff,"0X%X%X", tab_reg[i+1], tab_reg[i]);   //I need to swap the words, as the response is with the opposite endianness.
    printf("HEX: %s",buff);                 //Show the word-swapped string
    val.i = atof(buff);                     //Convert string to float :-)
    printf("\nFloat: %f\n", val.f);         //show the value in float

}

输出:

Raw1: 508C
Raw2: 436A
HEX: 0X436A508C
Float: 234.314636

2

愚蠢简单的例子:

unsigned char* floatToHex(float val){
    unsigned char* hexVals = malloc(sizeof(float));
    hexVals[0] = ((unsigned char*)&val)[0];
    hexVals[1] = ((unsigned char*)&val)[1];
    hexVals[2] = ((unsigned char*)&val)[2];
    hexVals[3] = ((unsigned char*)&val)[3];
    return hexVals;
}

当我想出这个方法时,它非常显而易见。不需要位掩码、memcpy或其他技巧。

在上面的例子中,它是为特定目的而设计的,我知道浮点数是32位的。如果您不确定系统,更好的解决方案是:

unsigned char* floatToHex(float val){
    unsigned char* hexVals = malloc(sizeof(float));
    for(int i = 0; i < sizeof(float); i++){
        hexVals[i] = ((unsigned char*)&val)[i];
    }
    return hexVals;
}

2

这种方法一直对我非常有效:

union converter{
float f_val;
unsigned int u_val;
};

union converter a;
a.f_val = 123.1443f;
printf("my hex value %x \n", a.u_val);

1

这个怎么样?

int main(void){
  float f = 28834.38282;
  char *x = (char *)&f; 
  printf("%f = ", f);

  for(i=0; i<sizeof(float); i++){ 
    printf("%02X ", *x++ & 0x0000FF); 
  }

  printf("\n");
}

1

0

对我最终起作用的方法(虽然看起来有些复杂):

#include <stdio.h>
int main((int argc, char** argv)
{
    float flt = 1234.56789;
    FILE *fout;
    fout = fopen("outFileName.txt","w");
    fprintf(fout, "%08x\n", *((unsigned long *)&flt);
        /* or */
    printf("%08x\n", *((unsigned long *)&flt);
}

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