IEEE float hex 424ce027 to float?
If I have a IEEE float hex 424ce027, how do I convert it to decimal?
unsigned char ptr[] = {0x42,0x4c,0xe0,0x27};
how 开发者_如何转开发do ?
float tmp = 51.218899;
If you don't want to assume endian and break all sorts of aliasing rules, this kind of thing works well:
union IntFloat {
uint32_t i;
float f;
};
...
union IntFloat val;
val.i = 0x424ce027;
printf("%f\n", val.f);
Still assuming 'float' is 32 bit, and your machine is IEEE-754 compliant, and has matching endian to integers, but we can probably stop being pedantic there.
For converting an integer value I'd do very similar to John Ripley's answer, but a slightly different mechanism. The same caveats apply - float and int must be the same size and endianness, and float must be IEEE if the initial hex value is to be treated as IEEE:
float tmp;
unsigned int src = 0x424ce027;
std::memcpy(&tmp, &src, sizeof tmp);
You ask how to convert to float, then how to convert to decimal. Floats are not decimal. To convert a float to a decimal string, you need something like printf("%g", tmp);
If you begin with an array of unsigned char rather than an int, then any direct-copying from the array needs the array to have been populated with the same endianness as on your platform. Your array is big-endian, but Intel is little-endian. So you could reverse
the array as in JohnB's answer if you know the endianness of your platform is the opposite of the endianness of the array. If you know the array is big-endian, and you don't know what endianness your platform is, you can do this (assuming 8 bits per char in the char array):
unsigned int src = 0;
for (int i = 0; i < sizeof src; ++i) {
src = (src << 8) + ptr[i];
}
Then continue as before.
The only portable way is to analyze the bit patterns and form the float manually inhering to all rules of ieee-754 floating points:
#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)
Caveat: This is not guaranteed to work in Standard C (floating point numbers technically do not have to be IEEE754 binary32/64; they could be a decimal implementation). However, since C99, the standard has declared that IEEE754-style ("IEC 60559") floating point representation is normative and defines it in Annex F. (And in practice, most hardware you'll encounter uses IEEE754 already.)
This should compile and be correct (with some assertions to be somewhat confident you're dealing with IEEE754 binary32 floats) on such hardware:
#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));
Which gives the output:
51.218899
Ref: http://lists.llvm.org/pipermail/llvm-bugs/2017-January/053186.html
Perhaps...
float f = *reinterpret_cast<float*>(ptr);
Although on my x86 machine here I had to also reverse the byte order of the character to get the value you wanted.
std::reverse(ptr, ptr + 4);
float f = *reinterpret_cast<float*>(ptr);
You might want to use sizeof(float) instead of 4 or some other way to get the size. You might want to reverse a copy of the bytes, not the original. It's somewhat ugly however you do it.
edit: As pointed out in the comments, this code is unsafe as it creates two pointers aliasing the same memory but of different types. It may well work on a specific compiler & program, but isn't guarenteed to by the standard.
unsigned char 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;
精彩评论