<div dir="ltr">On 21 January 2013 00:49, Chad Versace <span dir="ltr"><<a href="mailto:chad.versace@linux.intel.com" target="_blank">chad.versace@linux.intel.com</a>></span> wrote:<br><div class="gmail_extra"><div class="gmail_quote">

<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Not all float32 values can be exactly represented as a float16.<br>
_mesa_float_to_half() rounded such intermediate float32 values to zero by<br>
truncating unrepresentable bits in the mantissa. This behavior is bia<br></blockquote><div><br></div><div>Sentence fragment above.  Did you lose your place while editing the commit message?<br><br></div><div>With the commit mesage fixed, this patch is:<br>
<br></div><div>Reviewed-by: Paul Berry <<a href="mailto:stereotype441@gmail.com">stereotype441@gmail.com</a>><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">


<br>
This patch improves _mesa_float_to_half() by rounding intermediate float32<br>
values to the nearest float16; when the float32 is exactly between two<br>
float16 values we round to the one with an even mantissa. This behavior is<br>
preferred over the old behavior because:<br>
  - It has reduced bias relative to the old behavior.<br>
<br>
  - It reproduces the behavior of real hardware: opcode F32TO16 in<br>
    Intel's GPU ISA.<br>
<br>
  - By reproducing the behavior of the GPU (at least on Intel hardware),<br>
    compile-time evaluation of constant packHalf2x16 GLSL expressions will<br>
    result in the same value as if the expression were executed on the GPU.<br>
<br>
CC: Paul Berry <<a href="mailto:stereotype441@gmail.com" target="_blank">stereotype441@gmail.com</a>><br>
Signed-off-by: Chad Versace <<a href="mailto:chad.versace@linux.intel.com" target="_blank">chad.versace@linux.intel.com</a>><br>
---<br>
 src/mesa/main/imports.c | 66 +++++++++++++++++++++++++++----------------------<br>
 1 file changed, 37 insertions(+), 29 deletions(-)<br>
<br>
diff --git a/src/mesa/main/imports.c b/src/mesa/main/imports.c<br>
index 26c91dc..e6f7542 100644<br>
--- a/src/mesa/main/imports.c<br>
+++ b/src/mesa/main/imports.c<br>
@@ -336,8 +336,21 @@ _mesa_round_to_even(float val)<br>
<br>
 /**<br>
  * Convert a 4-byte float to a 2-byte half float.<br>
- * Based on code from:<br>
- * <a href="http://www.opengl.org/discussion_boards/ubb/Forum3/HTML/008786.html" target="_blank">http://www.opengl.org/discussion_boards/ubb/Forum3/HTML/008786.html</a><br>
+ *<br>
+ * Not all float32 values can be represented exactly as a float16 value. We<br>
+ * round such intermediate float32 values to the nearest float16. When the<br>
+ * float32 lies exactly between to float16 values, we round to the one with<br>
+ * an even mantissa.<br>
+ *<br>
+ * This rounding behavior has several benefits:<br>
+ *   - It has no sign bias.<br>
+ *<br>
+ *   - It reproduces the behavior of real hardware: opcode F32TO16 in Intel's<br>
+ *     GPU ISA.<br>
+ *<br>
+ *   - By reproducing the behavior of the GPU (at least on Intel hardware),<br>
+ *     compile-time evaluation of constant packHalf2x16 GLSL expressions will<br>
+ *     result in the same value as if the expression were executed on the GPU.<br>
  */<br>
 GLhalfARB<br>
 _mesa_float_to_half(float val)<br>
@@ -376,32 +389,13 @@ _mesa_float_to_half(float val)<br>
    else {<br>
       /* regular number */<br>
       const int new_exp = flt_e - 127;<br>
-      if (new_exp < -24) {<br>
-         /* this maps to 0 */<br>
-         /* m = 0; - already set */<br>
-         e = 0;<br>
-      }<br>
-      else if (new_exp < -14) {<br>
-         /* this maps to a denorm */<br>
-         unsigned int exp_val = (unsigned int) (-14 - new_exp); /* 2^-exp_val*/<br>
+      if (new_exp < -14) {<br>
+         /* The float32 lies in the range (0.0, min_normal16) and is rounded<br>
+          * to a nearby float16 value. The result will be either zero, subnormal,<br>
+          * or normal.<br>
+          */<br>
          e = 0;<br>
-         switch (exp_val) {<br>
-            case 0:<br>
-               _mesa_warning(NULL,<br>
-                   "float_to_half: logical error in denorm creation!\n");<br>
-               /* m = 0; - already set */<br>
-               break;<br>
-            case 1: m = 512 + (flt_m >> 14); break;<br>
-            case 2: m = 256 + (flt_m >> 15); break;<br>
-            case 3: m = 128 + (flt_m >> 16); break;<br>
-            case 4: m = 64 + (flt_m >> 17); break;<br>
-            case 5: m = 32 + (flt_m >> 18); break;<br>
-            case 6: m = 16 + (flt_m >> 19); break;<br>
-            case 7: m = 8 + (flt_m >> 20); break;<br>
-            case 8: m = 4 + (flt_m >> 21); break;<br>
-            case 9: m = 2 + (flt_m >> 22); break;<br>
-            case 10: m = 1; break;<br>
-         }<br>
+         m = _mesa_round_to_even((1 << 24) * fabsf(fi.f));<br>
       }<br>
       else if (new_exp > 15) {<br>
          /* map this value to infinity */<br>
@@ -409,12 +403,26 @@ _mesa_float_to_half(float val)<br>
          e = 31;<br>
       }<br>
       else {<br>
-         /* regular */<br>
+         /* The float32 lies in the range<br>
+          *   [min_normal16, max_normal16 + max_step16)<br>
+          * and is rounded to a nearby float16 value. The result will be<br>
+          * either normal or infinite.<br>
+          */<br>
          e = new_exp + 15;<br>
-         m = flt_m >> 13;<br>
+         m = _mesa_round_to_even(flt_m / (float) (1 << 13));<br>
       }<br>
    }<br>
<br>
+   assert(0 <= m && m <= 1024);<br>
+   if (m == 1024) {<br>
+      /* The float32 was rounded upwards into the range of the next exponent,<br>
+       * so bump the exponent. This correctly handles the case where f32<br>
+       * should be rounded up to float16 infinity.<br>
+       */<br>
+      ++e;<br>
+      m = 0;<br>
+   }<br>
+<br>
    result = (s << 15) | (e << 10) | m;<br>
    return result;<br>
 }<br>
<span><font color="#888888">--<br>
1.8.1.1<br>
<br>
</font></span></blockquote></div><br></div></div>