Use float<->half conversion routines from the OpenCL headers (#884)

* Use float<->half conversion routines from the OpenCL headers

Fixes #870

Signed-off-by: Kevin Petit <kevin.petit@arm.com>

* Use cl_half_from_double

* Fix windows build errors

* Fix more build errors

* Code formatting

* Remove TEST class
This commit is contained in:
Kévin Petit
2020-08-14 13:50:14 +01:00
committed by GitHub
parent 655d83db80
commit ed50fcad2d
15 changed files with 64 additions and 941 deletions

View File

@@ -14,11 +14,7 @@
// limitations under the License.
//
#include "helpers.h"
#include "../harness/imageHelpers.h"
// convert_float_to_half and convert_half_to_float may be found in test_conformance/images/image_helpers.cpp
cl_ushort convert_float_to_half( cl_float f );
cl_float convert_half_to_float( cl_ushort h );
#include "harness/imageHelpers.h"
#if defined( __APPLE__ )
#include <OpenGL/glu.h>

View File

@@ -961,49 +961,6 @@ const char *GetGLFormatName( GLenum format )
}
}
cl_ushort float2half_rte( float f )
{
union{ float f; cl_uint u; } u = {f};
cl_uint sign = (u.u >> 16) & 0x8000;
float x = fabsf(f);
//Nan
if( x != x )
{
u.u >>= (24-11);
u.u &= 0x7fff;
u.u |= 0x0200; //silence the NaN
return u.u | sign;
}
// overflow
if( x >= MAKE_HEX_FLOAT(0x1.ffep15f, 0x1ffeL, 3) )
return 0x7c00 | sign;
// underflow
if( x <= MAKE_HEX_FLOAT(0x1.0p-25f, 0x1L, -25) )
return sign; // The halfway case can return 0x0001 or 0. 0 is even.
// very small
if( x < MAKE_HEX_FLOAT(0x1.8p-24f, 0x18L, -28) )
return sign | 1;
// half denormal
if( x < MAKE_HEX_FLOAT(0x1.0p-14f, 0x1L, -14) )
{
u.f = x * MAKE_HEX_FLOAT(0x1.0p-125f, 0x1L, -125);
return sign | u.u;
}
u.f *= MAKE_HEX_FLOAT(0x1.0p13f, 0x1L, 13);
u.u &= 0x7f800000;
x += u.f;
u.f = x - u.f;
u.f *= MAKE_HEX_FLOAT(0x1.0p-112f, 0x1L, -112);
return (u.u >> (24-11)) | sign;
}
void* CreateRandomData( ExplicitType type, size_t count, MTdata d )
{
switch(type)
@@ -1100,7 +1057,8 @@ void* CreateRandomData( ExplicitType type, size_t count, MTdata d )
for( size_t i = 0; i < count; i++ )
{
p[ i ] = float2half_rte(get_random_float( 0.f, 1.f, d ));
p[i] = cl_half_from_float(get_random_float(0.f, 1.f, d),
CL_HALF_RTE);
}
return (void*)p;

View File

@@ -22,6 +22,8 @@
#include "parseParameters.h"
#include <CL/cl_half.h>
const char *IGetErrorString( int clErrorCode )
{
switch( clErrorCode )
@@ -287,7 +289,6 @@ const char *GetDataVectorString( void *dataBuffer, size_t typeSize, size_t vecSi
#endif
static float Ulp_Error_Half_Float( float test, double reference );
static inline float half2float( cl_ushort half );
// taken from math tests
#define HALF_MIN_EXP -13
@@ -334,49 +335,9 @@ static float Ulp_Error_Half_Float( float test, double reference )
return (float) scalbn( testVal - reference, ulp_exp );
}
// Taken from vLoadHalf test
static inline float half2float( cl_ushort us )
{
uint32_t u = us;
uint32_t sign = (u << 16) & 0x80000000;
int32_t exponent = (u & 0x7c00) >> 10;
uint32_t mantissa = (u & 0x03ff) << 13;
union{ unsigned int u; float f;}uu;
if( exponent == 0 )
{
if( mantissa == 0 )
return sign ? -0.0f : 0.0f;
int shift = __builtin_clz( mantissa ) - 8;
exponent -= shift-1;
mantissa <<= shift;
mantissa &= 0x007fffff;
}
else
if( exponent == 31)
{
uu.u = mantissa | sign;
if( mantissa )
uu.u |= 0x7fc00000;
else
uu.u |= 0x7f800000;
return uu.f;
}
exponent += 127 - 15;
exponent <<= 23;
exponent |= mantissa;
uu.u = exponent | sign;
return uu.f;
}
float Ulp_Error_Half( cl_ushort test, float reference )
{
return Ulp_Error_Half_Float( half2float(test), reference );
return Ulp_Error_Half_Float(cl_half_to_float(test), reference);
}

View File

@@ -30,9 +30,6 @@
RoundingMode gFloatToHalfRoundingMode = kDefaultRoundingMode;
static cl_ushort float2half_rte( float f );
static cl_ushort float2half_rtz( float f );
cl_device_type gDeviceType = CL_DEVICE_TYPE_DEFAULT;
bool gTestRounding = false;
double
@@ -911,72 +908,12 @@ int get_format_min_int( cl_image_format *format )
}
}
float convert_half_to_float( unsigned short halfValue )
{
// We have to take care of a few special cases, but in general, we just extract
// the same components from the half that exist in the float and re-stuff them
// For a description of the actual half format, see http://en.wikipedia.org/wiki/Half_precision
// Note: we store these in 32-bit ints to make the bit manipulations easier later
int sign = ( halfValue >> 15 ) & 0x0001;
int exponent = ( halfValue >> 10 ) & 0x001f;
int mantissa = ( halfValue ) & 0x03ff;
// Note: we use a union here to be able to access the bits of a float directly
union
{
unsigned int bits;
float floatValue;
} outFloat;
// Special cases first
if( exponent == 0 )
{
if( mantissa == 0 )
{
// If both exponent and mantissa are 0, the number is +/- 0
outFloat.bits = sign << 31;
return outFloat.floatValue; // Already done!
}
// If exponent is 0, it's a denormalized number, so we renormalize it
// Note: this is not terribly efficient, but oh well
while( ( mantissa & 0x00000400 ) == 0 )
{
mantissa <<= 1;
exponent--;
}
// The first bit is implicit, so we take it off and inc the exponent accordingly
exponent++;
mantissa &= ~(0x00000400);
}
else if( exponent == 31 ) // Special-case "numbers"
{
// If the exponent is 31, it's a special case number (+/- infinity or NAN).
// If the mantissa is 0, it's infinity, else it's NAN, but in either case, the packing
// method is the same
outFloat.bits = ( sign << 31 ) | 0x7f800000 | ( mantissa << 13 );
return outFloat.floatValue;
}
// Plain ol' normalized number, so adjust to the ranges a 32-bit float expects and repack
exponent += ( 127 - 15 );
mantissa <<= 13;
outFloat.bits = ( sign << 31 ) | ( exponent << 23 ) | mantissa;
return outFloat.floatValue;
}
cl_ushort convert_float_to_half( float f )
{
switch( gFloatToHalfRoundingMode )
{
case kRoundToNearestEven:
return float2half_rte( f );
case kRoundTowardZero:
return float2half_rtz( f );
case kRoundToNearestEven: return cl_half_from_float(f, CL_HALF_RTE);
case kRoundTowardZero: return cl_half_from_float(f, CL_HALF_RTZ);
default:
log_error( "ERROR: Test internal error -- unhandled or unknown float->half rounding mode.\n" );
exit(-1);
@@ -985,173 +922,6 @@ cl_ushort convert_float_to_half( float f )
}
cl_ushort float2half_rte( float f )
{
union{ float f; cl_uint u; } u = {f};
cl_uint sign = (u.u >> 16) & 0x8000;
float x = fabsf(f);
//Nan
if( x != x )
{
u.u >>= (24-11);
u.u &= 0x7fff;
u.u |= 0x0200; //silence the NaN
return u.u | sign;
}
// overflow
if( x >= MAKE_HEX_FLOAT(0x1.ffep15f, 0x1ffeL, 3) )
return 0x7c00 | sign;
// underflow
if( x <= MAKE_HEX_FLOAT(0x1.0p-25f, 0x1L, -25) )
return sign; // The halfway case can return 0x0001 or 0. 0 is even.
// very small
if( x < MAKE_HEX_FLOAT(0x1.8p-24f, 0x18L, -28) )
return sign | 1;
// half denormal
if( x < MAKE_HEX_FLOAT(0x1.0p-14f, 0x1L, -14) )
{
u.f = x * MAKE_HEX_FLOAT(0x1.0p-125f, 0x1L, -125);
return sign | u.u;
}
u.f *= MAKE_HEX_FLOAT(0x1.0p13f, 0x1L, 13);
u.u &= 0x7f800000;
x += u.f;
u.f = x - u.f;
u.f *= MAKE_HEX_FLOAT(0x1.0p-112f, 0x1L, -112);
return (u.u >> (24-11)) | sign;
}
cl_ushort float2half_rtz( float f )
{
union{ float f; cl_uint u; } u = {f};
cl_uint sign = (u.u >> 16) & 0x8000;
float x = fabsf(f);
//Nan
if( x != x )
{
u.u >>= (24-11);
u.u &= 0x7fff;
u.u |= 0x0200; //silence the NaN
return u.u | sign;
}
// overflow
if( x >= MAKE_HEX_FLOAT(0x1.0p16f, 0x1L, 16) )
{
if( x == INFINITY )
return 0x7c00 | sign;
return 0x7bff | sign;
}
// underflow
if( x < MAKE_HEX_FLOAT(0x1.0p-24f, 0x1L, -24) )
return sign; // The halfway case can return 0x0001 or 0. 0 is even.
// half denormal
if( x < MAKE_HEX_FLOAT(0x1.0p-14f, 0x1L, -14) )
{
x *= MAKE_HEX_FLOAT(0x1.0p24f, 0x1L, 24);
return (cl_ushort)((int) x | sign);
}
u.u &= 0xFFFFE000U;
u.u -= 0x38000000U;
return (u.u >> (24-11)) | sign;
}
class TEST
{
public:
TEST();
};
static TEST t;
void __vstore_half_rte(float f, size_t index, uint16_t *p)
{
union{ unsigned int u; float f;} u;
u.f = f;
unsigned short r = (u.u >> 16) & 0x8000;
u.u &= 0x7fffffff;
if( u.u >= 0x33000000U )
{
if( u.u >= 0x47800000 )
{
if( u.u <= 0x7f800000 )
r |= 0x7c00;
else
{
r |= 0x7e00 | ( (u.u >> 13) & 0x3ff );
}
}
else
{
float x = u.f;
if( u.u < 0x38800000 )
u.u = 0x3f000000;
else
u.u += 0x06800000;
u.u &= 0x7f800000U;
x += u.f;
x -= u.f;
u.f = x * MAKE_HEX_FLOAT(0x1.0p-112f, 0x1L, -112);
u.u >>= 13;
r |= (unsigned short) u.u;
}
}
((unsigned short*)p)[index] = r;
}
TEST::TEST()
{
return;
union
{
float f;
uint32_t i;
} test;
uint16_t control, myval;
log_info(" &&&&&&&&&&&&&&&&&&&&&&&&&&&& TESTING HALFS &&&&&&&&&&&&&&&&&&&&\n" );
test.i = 0;
do
{
if( ( test.i & 0xffffff ) == 0 )
{
if( ( test.i & 0xfffffff ) == 0 )
log_info( "*" );
else
log_info( "." );
fflush(stdout);
}
__vstore_half_rte( test.f, 0, &control );
myval = convert_float_to_half( test.f );
if( myval != control )
{
log_info( "\n******** ERROR: MyVal %04x control %04x source %12.24f\n", myval, control, test.f );
log_info( " source bits: %08x %a\n", test.i, test.f );
float t, c;
c = convert_half_to_float( control );
t = convert_half_to_float( myval );
log_info( " converted control: %12.24f myval: %12.24f\n", c, t );
}
test.i++;
} while( test.i != 0 );
log_info("\n &&&&&&&&&&&&&&&&&&&&&&&&&&&& TESTING HALFS &&&&&&&&&&&&&&&&&&&&\n" );
}
cl_ulong get_image_size( image_descriptor const *imageInfo )
{
cl_ulong imageSize;
@@ -1497,7 +1267,7 @@ void read_image_pixel_float( void *imageData, image_descriptor *imageInfo,
{
cl_ushort *dPtr = (cl_ushort *)ptr;
for( i = 0; i < channelCount; i++ )
tempData[ i ] = convert_half_to_float( dPtr[ i ] );
tempData[i] = cl_half_to_float(dPtr[i]);
break;
}
@@ -2617,11 +2387,11 @@ void pack_image_pixel( float *srcVector, const cl_image_format *imageFormat, voi
{
case kRoundToNearestEven:
for( unsigned int i = 0; i < channelCount; i++ )
ptr[ i ] = float2half_rte( srcVector[ i ] );
ptr[i] = cl_half_from_float(srcVector[i], CL_HALF_RTE);
break;
case kRoundTowardZero:
for( unsigned int i = 0; i < channelCount; i++ )
ptr[ i ] = float2half_rtz( srcVector[ i ] );
ptr[i] = cl_half_from_float(srcVector[i], CL_HALF_RTZ);
break;
default:
log_error( "ERROR: Test internal error -- unhandled or unknown float->half rounding mode.\n" );
@@ -3070,8 +2840,8 @@ int DetectFloatToHalfRoundingMode( cl_command_queue q ) // Returns CL_SUCCESS
cl_ushort rtz_ref[count*4];
for( size_t i = 0; i < 4 * count; i++ )
{
rte_ref[i] = float2half_rte( inp[i] );
rtz_ref[i] = float2half_rtz( inp[i] );
rte_ref[i] = cl_half_from_float(inp[i], CL_HALF_RTE);
rtz_ref[i] = cl_half_from_float(inp[i], CL_HALF_RTZ);
}
// Verify that we got something in either rtz or rte mode

View File

@@ -41,6 +41,8 @@
#include "rounding_mode.h"
#include "clImageHelper.h"
#include <CL/cl_half.h>
extern cl_device_type gDeviceType;
extern bool gTestRounding;
@@ -154,7 +156,6 @@ size_t compute_mip_level_offset( image_descriptor * imageInfo , size_t lod);
template <class T> void read_image_pixel( void *imageData, image_descriptor *imageInfo,
int x, int y, int z, T *outData, int lod )
{
float convert_half_to_float( unsigned short halfValue );
size_t width_lod = imageInfo->width, height_lod = imageInfo->height, depth_lod = imageInfo->depth, slice_pitch_lod = 0/*imageInfo->slicePitch*/ , row_pitch_lod = 0/*imageInfo->rowPitch*/;
width_lod = ( imageInfo->width >> lod) ?( imageInfo->width >> lod):1;
@@ -278,7 +279,7 @@ template <class T> void read_image_pixel( void *imageData, image_descriptor *ima
{
cl_ushort *dPtr = (cl_ushort *)ptr;
for( i = 0; i < get_format_channel_count( format ); i++ )
tempData[ i ] = (T)convert_half_to_float( dPtr[ i ] );
tempData[i] = (T)cl_half_to_float(dPtr[i]);
break;
}
@@ -638,6 +639,7 @@ protected:
size_t mVecSize;
};
extern cl_ushort convert_float_to_half(float f);
extern int DetectFloatToHalfRoundingMode( cl_command_queue ); // Returns CL_SUCCESS on success
// sign bit: don't care, exponent: maximum value, significand: non-zero
@@ -649,9 +651,6 @@ static int inline is_half_denorm( cl_ushort half ){ return IsHalfSubnormal( half
// sign bit: don't care, exponent: zero, significand: zero
static int inline is_half_zero( cl_ushort half ){ return ( half & 0x7fff ) == 0; }
cl_ushort convert_float_to_half( cl_float f );
cl_float convert_half_to_float( cl_ushort h );
extern double sRGBmap(float fc);
#endif // _imageHelpers_h