# d3dx9: Implement D3DXFloat16to32Array and D3DXFloat32to16Array.

Misha Koshelev misha680 at gmail.com
Mon Feb 7 16:49:30 CST 2011

On Mon, Feb 7, 2011 at 2:14 PM, Stefan Dösinger <stefandoesinger at gmx.at> wrote:
> Am Montag 07 Februar 2011, 19:37:09 schrieb Misha Koshelev:
>> Stefan: I believe this is actually a _difference_ between Windows versions.
>>
>> As you will note on the full results from winetestbot as well as my
>> own testing on XP (what I _thought_ was SP3) is that only 3/9
>> platforms have this error. The rest pass fine. Thus, it seems a
>> variance in the tests.
> The results still don't make sense.
>
> Basically it seems to me that native d3dx9 lacks support for NaN and INF.
> Those magic numbers are created by making the highest possible exponent a
> special marker(thus reduce exp_max by 1 and make exp_max + 1 special). This is
> why halfs end up with 65536.0 as just not reachable number. The highest
> exponent is 15, the highest mantissa value is 1023. This gives value = 2^15 *
> (1 + 1023/1024) = 65504.
>
> However, it looks like Windows(all versions) treat e = 16 like regular
> numbers, allowing max numbers from -131008.0 to +131008.0. This is shown by
> the conversion of 0x7fff to 131008.0 and 0xffff to -131008.0, and the conversion
> of -INF to 0xffff (sign bit, exp=max+1=16, mantissa=max=1023). I suspect someone
> tested INF and saw that it was converted into something that looked like NaN
> and added a line like this:
>
> if(isinf(input)) return 0x7c00;
> or
> if(input > 65504) return 0x7c00;
>
> but didn't properly implement NaN and INF. Then someone realized such a check
> is just stupid given the lack of infrastructure and removed it. And since the
> code is maybe written in assembler the check got only removed from the 32 bit
> version, not the 64 bit one. (Notice how old windows versions and 64 bit ones
> return 0x7c00 and new 32 bit ones return 0x7fff).
>
> I'd suggest a few more tests: See what a number like 80000.0 is converted to.
> Look what happens to 65503, 65504, 65505, 65534, 65535, 65536. And see what
> values like 0x7c01,7c02, 7ffe, 7ffd are converted to.
>

Thanks Stefan.

I believe I've found that the threshold value is actually 65520.0 for
conversions from single to half precision (tests are still running but
I checked on my machine at least so the half_ver1 values are correct;
additionally, I tested half_ver2 without the negative numbers
previously on winetestbot
http://testbot.winehq.org/JobDetails.pl?Key=8995). The full test
results with the attached patch will be here:
https://testbot.winehq.org/JobDetails.pl?Key=9000

In any case, I have gotten the half to single precision conversion to
work in Wine by extending to 16 bits for the exponent (changing < 31
to < 32).

However, I am still having trouble with the single to half precision
and believe I am spinning my wheels a bit at this point.

Here are the errors I'm getting:

math.c:2249: Test failed: Got 7800, expected 7bff or 7bff for index 3.
math.c:2249: Test failed: Got ffff, expected fc00 or fce2 for index 8.
math.c:2249: Test failed: Got f800, expected fbff or fbff for index 11.
math.c:2249: Test failed: Got ffff, expected fc00 or fc00 for index 12.
math.c:2249: Test failed: Got ffff, expected fc00 or fc00 for index 13.
math.c:2249: Test failed: Got ffff, expected fc00 or fc00 for index 14.
math.c:2249: Test failed: Got ffff, expected fc00 or fc00 for index 15.
math.c:2249: Test failed: Got 6400, expected 7fff or 7fff for index 18.
math.c:2249: Test failed: Got 6400, expected ffff or ffff for index 19.

Any ideas?

Thanks
Misha
-------------- next part --------------
From c95736739d43ea38c20025da9cea6c464dd6ed9b Mon Sep 17 00:00:00 2001
From: Misha Koshelev <misha680 at gmail.com>
Date: Mon, 7 Feb 2011 17:39:44 -0500
Subject: test
To: wine-patches <wine-patches at winehq.org>

---
dlls/d3dx9_36/d3dx9_36.spec |    4 +-
dlls/d3dx9_36/math.c        |   90 +++++++++++++++++++++++++++++++++++++++++++
dlls/d3dx9_36/tests/math.c  |   49 +++++++++++++++++++++++
include/d3dx9math.h         |   18 +++++++++
include/d3dx9math.inl       |   31 +++++++++++++++
5 files changed, 190 insertions(+), 2 deletions(-)

diff --git a/dlls/d3dx9_36/d3dx9_36.spec b/dlls/d3dx9_36/d3dx9_36.spec
index cbb6d20..55fe0cb 100644
--- a/dlls/d3dx9_36/d3dx9_36.spec
+++ b/dlls/d3dx9_36/d3dx9_36.spec
@@ -130,8 +130,8 @@
@ stub D3DXFillVolumeTextureTX
@ stdcall D3DXFilterTexture(ptr ptr long long)
@ stdcall D3DXFindShaderComment(ptr long ptr ptr)
-@ stub D3DXFloat16To32Array
-@ stub D3DXFloat32To16Array
+@ stdcall D3DXFloat16To32Array(ptr ptr long)
+@ stdcall D3DXFloat32To16Array(ptr ptr long)
@ stub D3DXFrameAppendChild
@ stub D3DXFrameCalculateBoundingSphere
@ stub D3DXFrameDestroy
diff --git a/dlls/d3dx9_36/math.c b/dlls/d3dx9_36/math.c
index fdb5f92..5c27edb 100644
--- a/dlls/d3dx9_36/math.c
+++ b/dlls/d3dx9_36/math.c
@@ -1769,3 +1769,93 @@ D3DXVECTOR4* WINAPI D3DXVec4TransformArray(D3DXVECTOR4* out, UINT outstride, CON
}
return out;
}
+
+static inline unsigned short float_32_to_16(const float *in)
+{
+    int exp = 0;
+    float tmp = fabs(*in);
+    unsigned int mantissa;
+    unsigned short ret;
+
+    /* Deal with special numbers */
+    if (*((unsigned int *) in) == 0x00000000) return 0x0000;
+    if (*((unsigned int *) in) == 0x80000000) return 0x8000;
+    /* !!! CONFUSION HERE */
+    if (*in > 65520) return 0x7c00;
+    if (*in < -65520) return 0xFFFF;
+
+    if (tmp < powf(2, 10)) {
+        do
+        {
+            tmp = tmp * 2.0f;
+            exp--;
+        } while (tmp < powf(2, 10));
+    } else if (tmp >= powf(2, 11)) {
+        do
+        {
+            tmp /= 2.0f;
+            exp++;
+        } while (tmp >= powf(2, 11));
+    }
+
+    mantissa = (unsigned int) tmp;
+    if (tmp - mantissa >= 0.5f) mantissa++; /* round to nearest, away from zero */
+
+    exp += 10;  /* Normalize the mantissa */
+    exp += 15;  /* Exponent is encoded with excess 15 */
+
+    if (exp > 30) { /* too big */
+        ret = 0x7c00; /* INF */
+    } else if (exp <= 0) {
+        /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers */
+        while (exp <= 0) {
+            mantissa = mantissa >> 1;
+            exp++;
+        }
+        ret = mantissa & 0x3ff;
+    } else {
+        ret = (exp << 10) | (mantissa & 0x3ff);
+    }
+
+    ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
+    return ret;
+}
+
+D3DXFLOAT16 *WINAPI D3DXFloat32To16Array(D3DXFLOAT16 *pout, CONST FLOAT *pin, UINT n)
+{
+    unsigned int i;
+
+    for (i = 0; i < n; ++i) {
+        pout[i].value = float_32_to_16(&pin[i]);
+    }
+
+    return pout;
+}
+
+static inline float float_16_to_32(const unsigned short *in) {
+    const unsigned short s = ((*in) & 0x8000);
+    const unsigned short e = ((*in) & 0x7C00) >> 10;
+    const unsigned short m = (*in) & 0x3FF;
+    const float sgn = (s ? -1.0f : 1.0f);
+
+    if (e == 0) {
+        if (m == 0) return sgn * 0.0f; /* +0.0 or -0.0 */
+        else return sgn * powf(2, -14.0f) * ((float)m / 1024.0f);
+    } else if (e < 32) {
+        return sgn * powf(2, (float)e - 15.0f) * (1.0f + ((float)m / 1024.0f));
+    } else {
+        if(m == 0) return sgn / 0.0f; /* +INF / -INF */
+        else return 0.0f / 0.0f; /* NAN */
+    }
+}
+
+FLOAT *WINAPI D3DXFloat16To32Array(FLOAT *pout, CONST D3DXFLOAT16 *pin, UINT n)
+{
+    unsigned int i;
+
+    for (i = 0; i < n; ++i) {
+        pout[i] = float_16_to_32(&pin[i].value);
+    }
+
+    return pout;
+}
diff --git a/dlls/d3dx9_36/tests/math.c b/dlls/d3dx9_36/tests/math.c
index e455a96..3095ae9 100644
--- a/dlls/d3dx9_36/tests/math.c
+++ b/dlls/d3dx9_36/tests/math.c
@@ -21,6 +21,7 @@

#include "wine/test.h"
#include "d3dx9.h"
+#include <math.h>

#define ARRAY_SIZE 5

@@ -2216,6 +2217,53 @@ static void test_D3DXVec_Array(void)
compare_planes(exp_plane, out_plane);
}

+static void test_D3DXFloat_Array(void)
+{
+    unsigned int i;
+    void *out = NULL;
+    D3DXFLOAT16 half_res[22];
+    FLOAT single[22] =   { 80000.0f, 65503.0f, 65504.0f, 65520.0f, 65521.0f, 65534.0f, 65535.0f, 65536.0f,
+                           -80000.0f, -65503.0f, -65504.0f, -65520.0f, -65521.0f, -65534.0f, -65535.0f, -65536.0f,
+                           INFINITY, -INFINITY, NAN, -NAN, 0.0f, -0.0f },
+        single_exp[22] = { 65536.0f, 65504.0f, 65504.0f, 65504.0f, 65536.0f, 65536.0f, 65535.0f, 65536.0f,
+                           -65536.0f, -65504.0f, -65504.0f, -65504.0f, -65536.0f, -65536.0f, -65535.0f, -65536.0f,
+                           65536.0f, -131008.0f, 131008.0f, -131008.0f, 0.0f, -0.0f },
+        single_res[22];
+    /* index 0 is 0x7ce2 on WXPPROSP3 (32 bit math), WVISTAADM (32 bit math), W7PRO (32 bit math),
+       index 8 is 0fce2 */
+    WORD half_ver1[22] = { 0x7c00, 0x7bff, 0x7bff, 0x7bff, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
+                           0xfc00, 0xfbff, 0xfbff, 0xfbff, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
+                           0x7c00, 0xffff, 0x7fff, 0xffff, 0x0, 0x8000 },
+         half_ver2[22] = { 0x7ce2, 0x7bff, 0x7bff, 0x7bff, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
+                           0xfce2, 0xfbff, 0xfbff, 0xfbff, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
+                           0x7fff, 0xffff, 0x7fff, 0xffff, 0x0, 0x8000 };
+
+    /* exception on NULL out or in parameter */
+    out = D3DXFloat32To16Array(half_res, single, 0);
+    ok(out == half_res, "Got %p, expected %p.\n", out, half_res);
+
+    out = D3DXFloat32To16Array(half_res, single, 22);
+    ok(out == half_res, "Got %p, expected %p.\n", out, half_res);
+
+    for (i = 0; i < 22; i++) {
+        ok(half_res[i].value == half_ver1[i] ||
+           half_res[i].value == half_ver2[i], "Got %x, expected %x or %x for index %d.\n",
+           half_res[i].value, half_ver1[i], half_ver2[i], i);
+    }
+
+    /* exception on NULL out or in parameter */
+    out = D3DXFloat16To32Array(single_res, (D3DXFLOAT16 *)half_ver1, 0);
+    ok(out == single_res, "Got %p, expected %p.\n", out, single_res);
+
+    out = D3DXFloat16To32Array(single_res, (D3DXFLOAT16 *)half_ver1, 22);
+    ok(out == single_res, "Got %p, expected %p.\n", out, single_res);
+
+    for (i = 0; i < 22; i++) {
+        ok(relative_error(single_res[i], single_exp[i]) < admitted_error, "Got %f, expected %f for index %d.\n",
+           single_res[i], single_exp[i], i);
+    }
+}
+
START_TEST(math)
{
D3DXColorTest();
@@ -2231,4 +2279,5 @@ START_TEST(math)
test_Matrix_Decompose();
test_Matrix_Transformation2D();
test_D3DXVec_Array();
+    test_D3DXFloat_Array();
}
diff --git a/include/d3dx9math.h b/include/d3dx9math.h
index f842e3e..cdb1deb 100644
--- a/include/d3dx9math.h
+++ b/include/d3dx9math.h
@@ -261,6 +261,21 @@ typedef struct D3DXCOLOR
FLOAT r, g, b, a;
} D3DXCOLOR, *LPD3DXCOLOR;

+typedef struct D3DXFLOAT16
+{
+#ifdef __cplusplus
+    D3DXFLOAT16();
+    D3DXFLOAT16(FLOAT f);
+    D3DXFLOAT16(CONST D3DXFLOAT16 &f);
+
+    operator FLOAT ();
+
+    BOOL operator == (CONST D3DXFLOAT16 &) const;
+    BOOL operator != (CONST D3DXFLOAT16 &) const;
+#endif /* __cplusplus */
+    WORD value;
+} D3DXFLOAT16, *LPD3DXFLOAT16;
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -358,6 +373,9 @@ D3DXVECTOR4* WINAPI D3DXVec4Normalize(D3DXVECTOR4 *pout, CONST D3DXVECTOR4 *pv);
D3DXVECTOR4* WINAPI D3DXVec4Transform(D3DXVECTOR4 *pout, CONST D3DXVECTOR4 *pv, CONST D3DXMATRIX *pm);
D3DXVECTOR4* WINAPI D3DXVec4TransformArray(D3DXVECTOR4 *pout, UINT outstride, CONST D3DXVECTOR4 *pv, UINT vstride, CONST D3DXMATRIX *pm, UINT n);

+D3DXFLOAT16 *WINAPI D3DXFloat32To16Array(D3DXFLOAT16 *pout, CONST FLOAT *pin, UINT n);
+FLOAT *WINAPI D3DXFloat16To32Array(FLOAT *pout, CONST D3DXFLOAT16 *pin, UINT n);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/d3dx9math.inl b/include/d3dx9math.inl
index 3cd078a..3f55aef 100644
--- a/include/d3dx9math.inl
+++ b/include/d3dx9math.inl
@@ -851,6 +851,37 @@ inline BOOL D3DXCOLOR::operator != (CONST D3DXCOLOR& col) const
return r != col.r || g != col.g || b != col.b || a != col.a;
}

+inline D3DXFLOAT16::D3DXFLOAT16()
+{
+}
+
+inline D3DXFLOAT16::D3DXFLOAT16(FLOAT f)
+{
+    D3DXFloat32To16Array(this, &f, 1);
+}
+
+inline D3DXFLOAT16::D3DXFLOAT16(CONST D3DXFLOAT16 &f)
+{
+    value = f.value;
+}
+
+inline D3DXFLOAT16::operator FLOAT ()
+{
+    FLOAT f;
+    D3DXFloat16To32Array(&f, this, 1);
+    return f;
+}
+
+inline BOOL D3DXFLOAT16::operator == (CONST D3DXFLOAT16 &f) const
+{
+    return value == f.value;
+}
+
+inline BOOL D3DXFLOAT16::operator != (CONST D3DXFLOAT16 &f) const
+{
+    return value != f.value;
+}
+
#endif /* __cplusplus */

/*_______________D3DXCOLOR_____________________*/
--
1.7.4