【发布时间】:2011-07-07 02:33:30
【问题描述】:
如果我有一个 IEEE 浮点十六进制 424ce027,如何将其转换为十进制?
unsigned char ptr[] = {0x42,0x4c,0xe0,0x27};
怎么办?
float tmp = 51.218899;
【问题讨论】:
如果我有一个 IEEE 浮点十六进制 424ce027,如何将其转换为十进制?
unsigned char ptr[] = {0x42,0x4c,0xe0,0x27};
怎么办?
float tmp = 51.218899;
【问题讨论】:
如果你不想假设 endian 并打破各种别名规则,这种事情很管用:
union IntFloat {
uint32_t i;
float f;
};
...
union IntFloat val;
val.i = 0x424ce027;
printf("%f\n", val.f);
仍然假设 'float' 是 32 位的,并且您的机器符合 IEEE-754 标准,并且具有与整数匹配的字节序,但我们可能不再那么迂腐了。
【讨论】:
对于转换整数值,我的做法与 John Ripley 的回答非常相似,但机制略有不同。相同的注意事项 - float 和 int 必须具有相同的大小和字节序,如果要将初始十六进制值视为 IEEE,则 float 必须是 IEEE:
float tmp;
unsigned int src = 0x424ce027;
std::memcpy(&tmp, &src, sizeof tmp);
你问如何转换为浮点数,然后如何转换为十进制。 浮点数不是小数。要将浮点数转换为十进制字符串,您需要类似printf("%g", tmp);
如果您从 unsigned char 数组而不是 int 数组开始,则从数组中直接复制的任何内容都需要使用与您的平台上相同的字节序填充数组。你的阵列是大端的,但英特尔是小端的。因此,如果您知道平台的字节序与数组的字节序相反,那么您可以像 JohnB 的回答那样 reverse 数组。如果你知道数组是大端的,并且你不知道你的平台是什么端,你可以这样做(假设 char 数组中每个字符 8 位):
unsigned int src = 0;
for (int i = 0; i < sizeof src; ++i) {
src = (src << 8) + ptr[i];
}
然后像以前一样继续。
【讨论】:
唯一可移植的方法是分析位模式并手动形成浮点数,以继承 ieee-754 浮点数的所有规则:
#define BIAS 150
unsigned char ptr[4] = {0x42,0x4c,0xe0,0x27};
// take care of endianness, which may vary between native float and native int
uint32_t tmp = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
uint32_t sign = tmp >> 31;
uint32_t exp = (tmp >> 23) & 0xff;
uint32_t mantissa = tmp & ((1 << 23) - 1);
float result;
if (exp > 0)
mantissa |= (1 << 23); // Handle denormals && Inf
// all integers < 16777216 can be represented exactly as a float
// multiply that by a power of two
result = ldexpf((float)mantissa, exp - BIAS); // in <math.h>
if (exp == 255 && mantissa != 0) // Produce NaN from Inf-Inf
result = (result - result);
if (sign) result = -result; // Flip sign (also handles -0.0f)
【讨论】:
警告:这不能保证在标准 C 中有效(从技术上讲,浮点数不必是 IEEE754 binary32/64;它们可能是十进制实现)。但是,从 C99 开始,该标准已声明 IEEE754 样式(“IEC 60559”)浮点表示是规范性的,并在附件 F 中对其进行了定义。(实际上,您将遇到的大多数硬件已经使用 IEEE754。)
这应该在这样的硬件上编译并且是正确的(通过一些断言来确定你正在处理 IEEE754 binary32 浮点数):
#include <float.h>
#include <stdint.h>
// C99 specifies a "__STDC_IEC_559__" which, if defined, means you
// can be really confident your implementation uses IEEE754 floats.
// Unfortunately, neither GCC nor Clang seems to actually define it.
// Instead, GCC defines a "__GCC_IEC_559", indicating support, and
// glibc on Linux provides a special magic stdc-predef.h header that
// GCC knows about and incorporates into compilation units without
// explicit inclusion.[1]
// So, long story short, if you're on Linux and use glibc and GCC,
// great; just check for __STDC_IEC_559__. Otherwise:
_Static_asssert(FLT_RADIX == 2, "IEEE754 floats have radix 2");
_Static_asssert(FLT_MANT_DIG == 24 && FLT_MIN_EXP == -125 &&
FLT_MAX_EXP == 128 && FLT_HAS_SUBNORM == 1,
"IEEE754 float characteristics");
_Static_assert(sizeof(float) == sizeof(uint32_t), "size");
float f;
uint32_t x = 0x424ce027UL;
memcpy(&f, &x, sizeof(f));
printf("%f\n", f));
它给出了输出:
51.218899
参考:http://lists.llvm.org/pipermail/llvm-bugs/2017-January/053186.html
【讨论】:
int 一样,这需要long 与float 的大小相同。就我个人而言,我更可能遇到long 太大(64 位 linux)的平台,而不是int 太小(大多数 16 位 CPU)的平台,但是 YMMV。我说“需要”,实际上它适用于 little-endian 64 位 linux,但在某种程度上,我将其描述为“从牙齿的皮肤”;-)
也许……
float f = *reinterpret_cast<float*>(ptr);
虽然在我的 x86 机器上,我还必须反转字符的字节顺序才能获得您想要的值。
std::reverse(ptr, ptr + 4);
float f = *reinterpret_cast<float*>(ptr);
您可能希望使用 sizeof(float) 而不是 4 或其他方式来获取大小。您可能想要反转字节的副本,而不是原始的。不管你怎么做,这有点难看。
edit:正如 cmets 中所指出的,这段代码是不安全的,因为它创建了两个指针,它们对同一内存进行别名,但类型不同。它可能适用于特定的编译器和程序,但不受标准的保证。
【讨论】:
float那样随机缓冲。
memcpy 是可以的,因为使用后目标对象的“有效类型”是float(它的声明类型),并且您以float 访问它,没问题。我认为这个 reinterpret_cast 从技术上讲是不行的,因为对象的“有效类型”是 char 数组,但是你以 float 的形式访问它。
无符号字符 ptr[] = {0x42,0x4c,0xe0,0x27};
float fTemp;
uint8_t *temp2 = (uint8_t *) &fTemp;
for (int i = 0; i < sizeof(float); i++)
temp2[i] = ptr[3-i];
std::cout<<"Data1: "<<fTemp;
【讨论】: