float -> unsigned int conversion problem

Hi,

In general, I have to convert an arbitrary positive float to unsigned int on both CPU and GPU getting same results.

On CPU the ‘out of range’ conversion (say, uint a = uint(1e50)) returns zero. On GPU the same conversion returns 0xcdcdcdcd (3452816845 dec). The GPU-like conversion is what I actually want to implement on both CPU and GPU (as CUDA Programming guide says, out of range conversions clamp to the end of available destination range).

As I run both CPU and GPU to make things faster, I have to make sure that same input will be handled similarly on both CPU and GPU. To say nothing of the precision and floating math problems, the other issue is conversion.

So the question is: how to convert arbitrary positive floats to uints to get same results on CPU and GPU ?

Since 1e50 does not fit into a float whatever you tested was not float to int conversion.

You will not get good speed anyway, and the results will depend on the compiler for the CPU case since the x86 hardware does not support float -> unsigned conversion.

You can use min and max to explictly clamp them to a valid range, but it will still be bad in the CPU case.

I have no idea about the GPU in this situation.

All right, let’s say not 1e50 but 3.402823466e+38F - maximal float possible. Does it change anything ?

This isn’t a CPU or GPU issue, the behavior of out of range conversion is documented as being undefined.

Section 6.3.1.4 of the C99 standard:

So you could have anything happen on any CPU, or something different happen on an Intel CPU versus an AMD one, or even different compilers.

But the workaround is trivial:

i=(unsigned int)x;

do something like:

i=(x>4294967295.0f ? 0xFFFFFFFF : (unsigned int)x);

Probably not. Since you use a constant you might be testing your compiler’s compile-time conversion and not the runtime-conversion anyway.

SPWorley basically answered it right, except that he forgot the x < 0 case.

We also do not know what is more important to you: having the full unsigned range or having fast code, most likely you can have only one of those.

No, I left that test out, since Romant said that the original float value is positive.

Original float is positive, right.

I need a code that would rapidly truncate positive float (possibly big float) to unsigned int (in fact, it is not too important will this truncation cover whole unsigned int range or not, it is not critical).

SPWorley’s solution looks very good, IMHO.

Thank you, guys!

Hm…

Hm, 4294967295 can not be represented by the integer type (only by the unsigned integer), so I am not sure this is sure to work.

More importantly though, 4294967295 can not be represented exactly as float, so you should replace the > by >=, otherwise it will not work for e.g. x = 4294967295.0f.

And sorry for missing the “positive” part before, but make sure that they indeed can not become negative e.g. due to rounding errors.

You’re correct about the representation problem, the compiler will silently change the 4294967295.0f into 4294967296.0f when compiled because of the quantization of the constant.

You’re right that >= would get around this, or changing the comparison constant to a representable value like 4294967040.0f (the largest representable float < 2^32).

Very interesting.

When trying to assign 0xffffffff to unsigned int inside the kernel, not 0xffffffff but 0xcdcdcdcd comes out.

Why ?!