<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>