Reinterpreting Data as Another Type
Consider the case where you want to mask off the sign bit of a floating-point type. There are multiple ways to solve this in C—using pointer aliasing, unions, or memcpy. Of these, only memcpy is strictly correct in C99. Because OpenCL C does not support memcpy, we need a different method to perform this masking-off operation. The general capability we need is the ability to reinterpret bits in a data type as another data type. In the example where we want to mask off the sign bit of a floating-point type, we want to reinterpret these bits as an unsigned integer type and then mask off the sign bit. Other examples include using the result of a vector relational operator and extracting the exponent or mantissa bits of a floating-point type.
The as_type and as_type n built-in functions allow you to reinterpret bits of a data type as another data type of the same size. The as_type is used for scalar data types (except bool and void) and as_type n for vector data types. double and half are supported only if the appropriate extensions are supported by the implementation.
The following example describes how you would mask off the sign bit of a floating-point type using the as_type built-in function:
float f; uint u; u = as_uint(f); f = as_float(u & ~(1 << 31));
If the operand and result type contain the same number of elements, the bits in the operand are returned directly without modification as the new type. If the operand and result type contain a different number of elements, two cases arise:
- The operand is a 4-component vector and the result is a 3-component vector. In this case, the xyz components of the operand and the result will have the same bits. The w component of the result is considered to be undefined.
- For all other cases, the behavior is implementation-defined.
We next describe a few examples that show how to use as_type and as_type n . The following example shows how to reinterpret an int as a float:
uint u = 0x3f800000; float f = as_float(u);
The variable u, which is declared as an unsigned integer, contains the value 0x3f800000. This represents the single-precision floating-point value 1.0. The variable f now contains the floating-point value 1.0.
In the next example, we reinterpret a float4 as an int4:
float4 f = (float4)(1.0f, 2.0f, 3.0f, 4.0f); int4 i = as_int4(f);
The variable i, defined to be of type int4, will have the following values in its xyzw components: 0x3f800000, 0x40000000, 0x40400000, 0x40800000.
The next example shows how we can perform the ternary selection operator (?:) for floating-point vector types using as_type n :
// Perform the operation f = f < g ? f : 0 for components of a // vector float4 f, g; int4 is_less = f < g; // Each component of the is_less vector will be 0 if result of < // operation is false and will be -1 (i.e., all bits set) if // the result of < operation is true. f = as_float4(as_int4(f) & is_less); // This basically selects f or 0 depending on the values in is_less.
The following example describes cases where the operand and result have a different number of results, in which case the behavior of as_type and as_type n is implementation-defined:
int i; short2 j = as_short2(i); // Legal. Result is implementation-defined int4 i; short8 j = as_short8(i); // Legal. Result is implementation-defined float4 f; float3 g = as_float3(f); // Legal. g.xyz will have same values as // f.xyz. g.w is undefined
This example describes reinterpreting a 4-component vector as a 3-component vector:
float4 f; float3 g = as_float3(f); // Legal. g.xyz will have same values as // f.xyz. g.w is undefined
The next example shows invalid ways of using as_type and as_type n , which should result in compilation errors:
float4 f; double4 g = as_double4(f); // Error. Result and operand have // different sizes. float3 f; float4 g = as_float4(f); // Error. Result and operand have // different sizes