Skip to content

Commit

Permalink
Fix testing of half-precision fma. (KhronosGroup#1882)
Browse files Browse the repository at this point in the history
Half-precision functions are generally tested against the
single-precision reference. This causes double rounding: first to single
precision, then from there to half precision. For the most part, it is
good enough, but specifically in the case of fma, a correctly rounded
result is required and is not obtained, for instance for arguments
0x1.eacp+7, 0x1.3f4p+4, 0x1.c04p+14, which produce an exact result of
0x1.065fffp+15 which should be rounded to half-prefcision 0x1.064p+15,
but was previously first rounded to single-precision 0x1.066p+15, and
from there to half-precision 0x1.068p+15. Testing against reference_fmal
gives us sufficient precision that double rounding does not cause
issues.

The f_fma(..., FLUSHED) calls for FTZ testing cannot be updated the same
way but do not need to be: these calls all have at least one constant
operand of zero. If one operand is zero, double rounding cannot be an
issue.
  • Loading branch information
hvdijk authored Feb 6, 2024
1 parent 79fc236 commit d338b42
Show file tree
Hide file tree
Showing 4 changed files with 10 additions and 9 deletions.
2 changes: 1 addition & 1 deletion test_common/harness/errorHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ static float Ulp_Error_Half_Float(float test, double reference)
return (float)scalbn(testVal - reference, ulp_exp);
}

float Ulp_Error_Half(cl_half test, float reference)
float Ulp_Error_Half(cl_half test, double reference)
{
return Ulp_Error_Half_Float(cl_half_to_float(test), reference);
}
Expand Down
2 changes: 1 addition & 1 deletion test_common/harness/errorHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ static int vlog_win32(const char *format, ...);

extern const char *IGetErrorString(int clErrorCode);

extern float Ulp_Error_Half(cl_half test, float reference);
extern float Ulp_Error_Half(cl_half test, double reference);
extern float Ulp_Error(float test, double reference);
extern float Ulp_Error_Double(double test, long double reference);

Expand Down
14 changes: 7 additions & 7 deletions test_conformance/math_brute_force/ternary_half.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,17 +237,17 @@ int TestFunc_Half_Half_Half_Half(const Func *f, MTdata d, bool relaxedMode)
for (size_t j = 0; j < bufferElements; j++)
{
feclearexcept(FE_OVERFLOW);
res[j] = HFF((float)f->func.f_fma(
HTF(hp0[j]), HTF(hp1[j]), HTF(hp2[j]), CORRECTLY_ROUNDED));
res[j] = HFD((double)f->dfunc.f_fff(HTF(hp0[j]), HTF(hp1[j]),
HTF(hp2[j])));
overflow[j] =
FE_OVERFLOW == (FE_OVERFLOW & fetestexcept(FE_OVERFLOW));
}
}
else
{
for (size_t j = 0; j < bufferElements; j++)
res[j] = HFF((float)f->func.f_fma(
HTF(hp0[j]), HTF(hp1[j]), HTF(hp2[j]), CORRECTLY_ROUNDED));
res[j] = HFD((double)f->dfunc.f_fff(HTF(hp0[j]), HTF(hp1[j]),
HTF(hp2[j])));
}

// Read the data back
Expand Down Expand Up @@ -277,9 +277,9 @@ int TestFunc_Half_Half_Half_Half(const Func *f, MTdata d, bool relaxedMode)
{
int fail;
cl_half test = ((cl_half *)q)[j];
float ref1 = f->func.f_fma(HTF(hp0[j]), HTF(hp1[j]),
HTF(hp2[j]), CORRECTLY_ROUNDED);
cl_half correct = HFF(ref1);
double ref1 = (double)f->dfunc.f_fff(
HTF(hp0[j]), HTF(hp1[j]), HTF(hp2[j]));
cl_half correct = HFD(ref1);

// Per section 10 paragraph 6, accept any result if an input
// or output is a infinity or NaN or overflow
Expand Down
1 change: 1 addition & 0 deletions test_conformance/math_brute_force/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ extern RoundingMode gFloatToHalfRoundingMode;
extern cl_half_rounding_mode gHalfRoundingMode;

#define HFF(num) cl_half_from_float(num, gHalfRoundingMode)
#define HFD(num) cl_half_from_double(num, gHalfRoundingMode)
#define HTF(num) cl_half_to_float(num)

#define LOWER_IS_BETTER 0
Expand Down

0 comments on commit d338b42

Please sign in to comment.