Sign in to follow this  
adt7

Can you split a 32bit float into 2 16bit floats?

Recommended Posts

I'm probably being incredibly stup here, but I'm having difficulty working this out in my own head. I know you can pack 2 16 bit floats into a 32 bit float, but is it possible to split a 32 bit float into 2 16bit floats without losing anything? If so, how would you go about doing so?

Share this post


Link to post
Share on other sites
If you want to divide a 32 bit value into two 16 bit values
try this -

float f;
WORD higherBits = HIWORD(DWORD(f));
WORD lowerBits = LOWORD(DWORD(F));

Hope that helps.

Share this post


Link to post
Share on other sites
Quote:
without losing anything?

Do you mean "and still have anything make sense?" [wink]

float16 f1 = somevalue, f2 = anothervalue;
float32 packed;
byte* ptrPacked = (byte*)&packed;
byte* inputPtr1 = (byte*)&f1;
byte* inputPtr2 = (byte*)&f2;
ptrPacked[0] = inputPtr1[0];
ptrPacked[1] = inputPtr1[1];
ptrPacked[2] = inputPtr2[0];
ptrPacked[3] = inputPtr2[1];
...
// packed is now garbage but can be unpacked

Share this post


Link to post
Share on other sites
It might help if I explain what I'm trying to do.

I want to try and store 32bit depth values (I guess technically 24bit) into an R16G16 target, if at all possible.

Buckeye, doesn't your example do the opposite? Packing two 16bit floats into a 32bit?

Share this post


Link to post
Share on other sites
Quote:
Original post by Chetanhl
If you want to divide a 32 bit value into two 16 bit values
try this -

float f;
WORD higherBits = HIWORD(DWORD(f));
WORD lowerBits = LOWORD(DWORD(F));

Hope that helps.


In DWORD(f), it will just be converted to integer value-wise, not bit-wise.

Share this post


Link to post
Share on other sites
Quote:
Buckeye, doesn't your example do the opposite?
Yep. Misread your statement. Just reverse the assignments.

float32 f32 = your_value;
float16 f1, f2;
//.. etc.
f1Ptr[0] = f32Ptr[0];
f1Ptr[1] = f32Ptr[1];
f2Ptr[0] = f32Ptr[2];
f2Ptr[1] = f32Ptr[3];

Quote:
without losing anything?

Be aware, f1 and f2 are garbage until recombined. Are you just trying to store the 32bit float in the target because R32 (or something similar) is not available or inconvenient?

Share this post


Link to post
Share on other sites
You are certainly free to take a 32-bit floating point value apart into two 16-bit values by putting the first 16 bits into one and the next into another. But the resulting values aren't meaningful on their own. They certainly aren't "16-bit floats"; C and C++ don't deal with those (there isn't widespread hardware support for them anyway, even if IEEE does define a standard for them). They're just two 16-bit-sized chunks of data which you can put back together to restore the original value.

Buckeye shows the basic approach, although you should avoid C-style casts in C++.

I'll make a generalized function for copying some bytes around, and then use it to implement what you need.


template <size_t offset, typename S, typename D>
void copyBytes(const S& source, D& destination) {
// Copy from source to destination at a given offset. The sizes of the
// source and destination types are compared, and the smaller type sets the
// number of bytes copied. The offset value offsets from the beginning of
// the larger type.
const char* source_ptr = reinterpret_cast<const char*>(&source);
char* dest_ptr = reinterpret_cast<char*>(&destination);

const int source_size = sizeof(S);
const int dest_size = sizeof(D);
const bool source_bigger = sizeof(S) > sizeof(D);
const int big_size = source_bigger ? source_size : dest_size;
const int small_size = source_bigger ? dest_size : source_size;

// Ensure that the code doesn't compile if the offset is bad.
char offset_is_too_large[big_size - small_size - offset];

// Do the copy.
if (source_bigger) { source_ptr += offset; }
else { dest_ptr += offset; }
// std::copy lives in <algorithm>.
std::copy(source_ptr, source_ptr + small_size, dest_ptr);
}

// used like
float original;
my_16bit_type high, low;
copyBytes<0>(original, high);
copyBytes<2>(original, low);
float reconstituted;
copyBytes<0>(high, reconstituted);
copyBytes<2>(low, reconstituted);

Share this post


Link to post
Share on other sites
On further reflection, here's a simpler way:


// The general approach is to use boost::array so that we're always dealing
// with single values instead of arrays, and have simple functions to copy from
// one to another.

template <typename S, typename D>
void reinterpret(const S& source, D& destination) {
const char* s = reinterpret_cast<const char*>(&source);
char* d = reinterpret_cast<char*>(&destination);
char source_is_too_large[sizeof(D) - sizeof(S)];
std::copy(s, s + sizeof(S), d);
}

template <typename D, typename S>
D reinterpreted(const S& source) {
D result;
reinterpret(source, result);
return result;
}

// Used like:
typedef boost::array<my_16bit_type, 2> segmented_float;
float original;
segmented_float segmented = reinterpreted<segmented_float>(original);
float reconstituted = reinterpreted<float>(segmented);



I factored it to have separate 'reinterpret' and 'reinterpreted' versions for convenience; use 'reinterpret' to read into an l-value that already exists (e.g. 'big_structure bs; reinterpret(original, bs.segments)'), and 'reinterpreted' to initialize a variable or for use in the middle of an expression (e.g. 'my_16bit_type high = reinterpreted<segmented_float>(original)[0]').

Warnings: this should only be used with POD types (fortunately, boost::array qualifies), and I haven't tested it (but it should be close if not correct already).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this