DO NOT MERGE: Ignore denormals in floatUlpThresholdCompare.

- Calculate ULPs assuming denormals (and signed zeros) do not exist.

Bug: 21731219
Change-Id: I6f426c781b2c38d7279b65a973e381f0848fbe02
diff --git a/framework/common/tcuImageCompare.cpp b/framework/common/tcuImageCompare.cpp
index 3bae0ef..5f9f67f 100644
--- a/framework/common/tcuImageCompare.cpp
+++ b/framework/common/tcuImageCompare.cpp
@@ -31,6 +31,7 @@
 #include "tcuRGBA.hpp"
 #include "tcuTexture.hpp"
 #include "tcuTextureUtil.hpp"
+#include "tcuFloat.hpp"
 
 #include <string.h>
 
@@ -386,6 +387,74 @@
 }
 
 /*--------------------------------------------------------------------*//*!
+ * Returns the index of float in a float space without denormals
+ * so that:
+ * 1) f(0.0) = 0
+ * 2) f(-0.0) = 0
+ * 3) f(b) = f(a) + 1  <==>  b = nextAfter(a)
+ *
+ * See computeFloatFlushRelaxedULPDiff for details
+ *//*--------------------------------------------------------------------*/
+static deInt32 getPositionOfIEEEFloatWithoutDenormals (float x)
+{
+	DE_ASSERT(!deIsNaN(x)); // not sane
+
+	if (x == 0.0f)
+		return 0;
+	else if (x < 0.0f)
+		return -getPositionOfIEEEFloatWithoutDenormals(-x);
+	else
+	{
+		DE_ASSERT(x > 0.0f);
+
+		const tcu::Float32 f(x);
+
+		if (f.isDenorm())
+		{
+			// Denorms are flushed to zero
+			return 0;
+		}
+		else
+		{
+			// sign is 0, and it's a normal number. Natural position is its bit
+			// pattern but since we've collapsed the denorms, we must remove
+			// the gap here too to keep the float enumeration continuous.
+			//
+			// Denormals occupy one exponent pattern. Removing one from
+			// exponent should to the trick.
+			return (deInt32)(f.bits() - (1u << 23u));
+		}
+	}
+}
+
+static deUint32 computeFloatFlushRelaxedULPDiff (float a, float b)
+{
+	if (deIsNaN(a) && deIsNaN(b))
+		return 0;
+	else if (deIsNaN(a) || deIsNaN(b))
+	{
+		return 0xFFFFFFFFu;
+	}
+	else
+	{
+		// Using the "definition 5" in Muller, Jean-Michel. "On the definition of ulp (x)" (2005)
+		// assuming a floating point space is IEEE single precision floating point space without
+		// denormals (and signed zeros).
+		const deInt32 aIndex = getPositionOfIEEEFloatWithoutDenormals(a);
+		const deInt32 bIndex = getPositionOfIEEEFloatWithoutDenormals(b);
+		return (deUint32)de::abs(aIndex - bIndex);
+	}
+}
+
+static tcu::UVec4 computeFlushRelaxedULPDiff (const tcu::Vec4& a, const tcu::Vec4& b)
+{
+	return tcu::UVec4(computeFloatFlushRelaxedULPDiff(a.x(), b.x()),
+					  computeFloatFlushRelaxedULPDiff(a.y(), b.y()),
+					  computeFloatFlushRelaxedULPDiff(a.z(), b.z()),
+					  computeFloatFlushRelaxedULPDiff(a.w(), b.w()));
+}
+
+/*--------------------------------------------------------------------*//*!
  * \brief Per-pixel threshold-based comparison
  *
  * This compare computes per-pixel differences between result and reference
@@ -393,7 +462,8 @@
  *
  * This comparison uses ULP (units in last place) metric for computing the
  * difference between floating-point values and thus this function can
- * be used only for comparing floating-point texture data.
+ * be used only for comparing floating-point texture data. In ULP calculation
+ * the denormal numbers are allowed to be flushed to zero.
  *
  * On failure error image is generated that shows where the failing pixels
  * are.
@@ -426,17 +496,10 @@
 		{
 			for (int x = 0; x < width; x++)
 			{
-				Vec4	refPix		= reference.getPixel(x, y, z);
-				Vec4	cmpPix		= result.getPixel(x, y, z);
-				UVec4	refBits;
-				UVec4	cmpBits;
-
-				// memcpy() is the way to do float->uint32 reinterpretation.
-				memcpy(refBits.getPtr(), refPix.getPtr(), 4*sizeof(deUint32));
-				memcpy(cmpBits.getPtr(), cmpPix.getPtr(), 4*sizeof(deUint32));
-
-				UVec4	diff		= abs(refBits.cast<int>() - cmpBits.cast<int>()).cast<deUint32>();
-				bool	isOk		= boolAll(lessThanEqual(diff, threshold));
+				const Vec4	refPix	= reference.getPixel(x, y, z);
+				const Vec4	cmpPix	= result.getPixel(x, y, z);
+				const UVec4	diff	= computeFlushRelaxedULPDiff(refPix, cmpPix);
+				const bool	isOk	= boolAll(lessThanEqual(diff, threshold));
 
 				maxDiff = max(maxDiff, diff);