Relax fma() verification in builtin_functions.common.fma
authorPyry Haulos <phaulos@google.com>
Thu, 19 Feb 2015 22:15:03 +0000 (14:15 -0800)
committerPyry Haulos <phaulos@google.com>
Fri, 20 Feb 2015 16:41:20 +0000 (08:41 -0800)
GL_EXT_gpu_shader5 allows fma() to be implemented either as a single, or
two correctly-rounded operations. Current tests assumed fma() for lowp
and mediump to be implemented as a single operation as no rounding was
allowed mid-operation.

This change relaxes fma() tests to allow for a*b+c expansion.

Bug: 19431850
Change-Id: I46eea74bf3b95bfdbc510e13ef7315854be623ee

modules/gles31/functional/es31fShaderCommonFunctionTests.cpp

index 3aec886..1bac102 100644 (file)
@@ -27,6 +27,8 @@
 #include "tcuTestLog.hpp"
 #include "tcuFormatUtil.hpp"
 #include "tcuFloat.hpp"
+#include "tcuInterval.hpp"
+#include "tcuFloatFormat.hpp"
 #include "deRandom.hpp"
 #include "deMath.h"
 #include "deString.h"
@@ -2020,32 +2022,57 @@ public:
                }
        }
 
+       static tcu::Interval fma (glu::Precision precision, float a, float b, float c)
+       {
+               const tcu::FloatFormat formats[] =
+               {
+                       //                               minExp         maxExp          mantissa        exact,          subnormals      infinities      NaN
+                       tcu::FloatFormat(0,                     0,                      7,                      false,          tcu::YES,       tcu::MAYBE,     tcu::MAYBE),
+                       tcu::FloatFormat(-13,           13,                     9,                      false,          tcu::MAYBE,     tcu::MAYBE,     tcu::MAYBE),
+                       tcu::FloatFormat(-126,          127,            23,                     true,           tcu::MAYBE, tcu::YES,   tcu::MAYBE)
+               };
+               const tcu::FloatFormat& format  = de::getSizedArrayElement<glu::PRECISION_LAST>(formats, precision);
+               const tcu::Interval             ia              = format.convert(a);
+               const tcu::Interval             ib              = format.convert(b);
+               const tcu::Interval             ic              = format.convert(c);
+               tcu::Interval                   prod0;
+               tcu::Interval                   prod1;
+               tcu::Interval                   prod2;
+               tcu::Interval                   prod3;
+               tcu::Interval                   prod;
+               tcu::Interval                   res;
+
+               TCU_SET_INTERVAL(prod0, tmp, tmp = ia.lo() * ib.lo());
+               TCU_SET_INTERVAL(prod1, tmp, tmp = ia.lo() * ib.hi());
+               TCU_SET_INTERVAL(prod2, tmp, tmp = ia.hi() * ib.lo());
+               TCU_SET_INTERVAL(prod3, tmp, tmp = ia.hi() * ib.hi());
+
+               prod = format.convert(format.roundOut(prod0 | prod1 | prod2 | prod3, ia.isFinite() && ib.isFinite()));
+
+               TCU_SET_INTERVAL_BOUNDS(res, tmp,
+                                                               tmp = prod.lo() + ic.lo(),
+                                                               tmp = prod.hi() + ic.hi());
+
+               return format.convert(format.roundOut(res, prod.isFinite() && ic.isFinite()));
+       }
+
        bool compare (const void* const* inputs, const void* const* outputs)
        {
                const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
                const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
                const int                               scalarSize              = glu::getDataTypeScalarSize(type);
-               const bool                              signedZero              = supportsSignedZero(precision);
-
-               const int                               mantissaBits    = getMinMantissaBits(precision);
 
                for (int compNdx = 0; compNdx < scalarSize; compNdx++)
                {
-                       const float             a                       = ((const float*)inputs[0])[compNdx];
-                       const float             b                       = ((const float*)inputs[1])[compNdx];
-                       const float             c                       = ((const float*)inputs[2])[compNdx];
-                       const float             res                     = ((const float*)outputs[0])[compNdx];
-                       const float             ref                     = a*b + c;
-
-                       const int               numBitsLost     = 1; // allow last bit to vary
-                       const deUint32  maxUlpDiff      = getMaxUlpDiffFromBits(de::max(0, mantissaBits-numBitsLost));
+                       const float                     a                       = ((const float*)inputs[0])[compNdx];
+                       const float                     b                       = ((const float*)inputs[1])[compNdx];
+                       const float                     c                       = ((const float*)inputs[2])[compNdx];
+                       const float                     res                     = ((const float*)outputs[0])[compNdx];
+                       const tcu::Interval     ref                     = fma(precision, a, b, c);
 
-                       const deUint32  ulpDiff         = signedZero ? getUlpDiff(res, ref) : getUlpDiffIgnoreZeroSign(res, ref);
-
-                       if (ulpDiff > maxUlpDiff)
+                       if (!ref.contains(res))
                        {
-                               m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold "
-                                                 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
+                               m_failMsg << "Expected [" << compNdx << "] = " << ref;
                                return false;
                        }
                }