[PATCH v4] dix: add utility functions for double to/fro FP1616/FP3232 conversion

Jeremy Huddleston jeremyhu at apple.com
Fri Oct 14 18:44:55 PDT 2011


Co-authored-by: Jeremy Huddleston <jeremyhu at apple.com>
Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
Reviewed-by: Jeremy Huddleston <jeremyhu at apple.com>
---
 dix/inpututils.c     |   63 ++++++++++++++++++++++++
 include/inpututils.h |    6 ++
 test/input.c         |  128 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 197 insertions(+), 0 deletions(-)

diff --git a/dix/inpututils.c b/dix/inpututils.c
index 0a3d3d8..cd45773 100644
--- a/dix/inpututils.c
+++ b/dix/inpututils.c
@@ -759,3 +759,66 @@ input_option_set_value(InputOption *opt, const char *value)
     if (value)
         opt->value = strdup(value);
 }
+
+
+/* FP1616/FP3232 conversion functions.
+ * Fixed point types are encoded as signed integral and unsigned frac. So any
+ * negative number -n.m is encoded as floor(n) + (1 - 0.m).
+ */
+double
+fp1616_to_double(FP1616 in)
+{
+    double ret;
+
+    ret  = (double)(in >> 16);
+    ret += (double)(in & 0xffff) * (1.0 / (1UL << 16)); /* Optimized: ldexp((double)(in & 0xffff), -16); */
+    return ret;
+}
+
+double
+fp3232_to_double(FP3232 in)
+{
+    double ret;
+    ret  = (double)in.integral;
+    ret += (double)in.frac * (1.0 / (1ULL << 32)); /* Optimized: ldexp((double)in.frac, -32); */
+    return ret;
+}
+
+
+FP1616
+double_to_fp1616(double in)
+{
+    FP1616 ret;
+    int32_t integral;
+    double tmp;
+    uint32_t frac_d;
+
+    tmp = floor(in);
+    integral = (int32_t)tmp;
+
+    tmp = (in - integral) * (1UL << 16); /* Optimized: ldexp(in - integral, 16) */
+    frac_d = (uint16_t)tmp;
+
+    ret = integral << 16;
+    ret |= frac_d & 0xffff;
+    return ret;
+}
+
+FP3232
+double_to_fp3232(double in)
+{
+    FP3232 ret;
+    int32_t integral;
+    double tmp;
+    uint32_t frac_d;
+
+    tmp = floor(in);
+    integral = (int32_t)tmp;
+
+    tmp = (in - integral) * (1ULL << 32); /* Optimized: ldexp(in - integral, 32) */
+    frac_d = (uint32_t)tmp;
+
+    ret.integral = integral;
+    ret.frac = frac_d;
+    return ret;
+}
diff --git a/include/inpututils.h b/include/inpututils.h
index 47e242d..2832ed5 100644
--- a/include/inpututils.h
+++ b/include/inpututils.h
@@ -30,6 +30,7 @@
 #define INPUTUTILS_H
 
 #include "input.h"
+#include <X11/extensions/XI2proto.h>
 
 struct _ValuatorMask {
     int8_t      last_bit; /* highest bit set in mask */
@@ -40,4 +41,9 @@ struct _ValuatorMask {
 extern void verify_internal_event(const InternalEvent *ev);
 extern void init_device_event(DeviceEvent *event, DeviceIntPtr dev, Time ms);
 
+FP3232 double_to_fp3232(double in);
+FP1616 double_to_fp1616(double in);
+double fp1616_to_double(FP1616 in);
+double fp3232_to_double(FP3232 in);
+
 #endif
diff --git a/test/input.c b/test/input.c
index afc4d4d..0bfef88 100644
--- a/test/input.c
+++ b/test/input.c
@@ -1462,9 +1462,137 @@ static void input_option_test(void)
     assert(list == NULL);
 }
 
+static void
+_test_double_fp16_values(double orig_d)
+{
+    FP1616 first_fp16, final_fp16;
+    double final_d;
+    char first_fp16_s[64];
+    char final_fp16_s[64];
+
+    if (orig_d > 0x7FFF) {
+        printf("Test out of range\n");
+        assert(0);
+    }
+
+    first_fp16 = double_to_fp1616(orig_d);
+    final_d = fp1616_to_double(first_fp16);
+    final_fp16 = double_to_fp1616(final_d);
+
+    snprintf(first_fp16_s, sizeof(first_fp16_s), "%d + %u * 2^-16", (first_fp16 & 0xffff0000) >> 16, first_fp16 & 0xffff);
+    snprintf(final_fp16_s, sizeof(final_fp16_s), "%d + %u * 2^-16", (final_fp16 & 0xffff0000) >> 16, final_fp16 & 0xffff);
+
+    printf("FP16: original double: %f first fp16: %s, re-encoded double: %f, final fp16: %s\n", orig_d, first_fp16_s, final_d, final_fp16_s);
+
+    /* since we lose precision, we only do rough range testing */
+    assert(final_d > orig_d - 0.1);
+    assert(final_d < orig_d + 0.1);
+
+    assert(memcmp(&first_fp16, &final_fp16, sizeof(FP1616)) == 0);
+
+    if (orig_d > 0)
+        _test_double_fp16_values(-orig_d);
+}
+
+static void
+_test_double_fp32_values(double orig_d)
+{
+    FP3232 first_fp32, final_fp32;
+    double final_d;
+
+    if (orig_d > 0x7FFFFFFF) {
+        printf("Test out of range\n");
+        assert(0);
+    }
+
+    first_fp32 = double_to_fp3232(orig_d);
+    final_d = fp3232_to_double(first_fp32);
+    final_fp32 = double_to_fp3232(final_d);
+
+    /* {
+     *     char first_fp32_s[64];
+     *     char final_fp32_s[64];
+     *     snprintf(first_fp32_s, sizeof(first_fp32_s), "%d + %u * 2^-32", first_fp32.integral, first_fp32.frac);
+     *     snprintf(final_fp32_s, sizeof(final_fp32_s), "%d + %u * 2^-32", first_fp32.integral, final_fp32.frac);
+     * 
+     *     printf("FP32: original double: %f first fp32: %s, re-encoded double: %f, final fp32: %s\n", orig_d, first_fp32_s, final_d, final_fp32_s);
+     * }
+     */
+
+    /* since we lose precision, we only do rough range testing */
+    assert(final_d > orig_d - 0.1);
+    assert(final_d < orig_d + 0.1);
+
+    assert(memcmp(&first_fp32, &final_fp32, sizeof(FP3232)) == 0);
+
+    if (orig_d > 0)
+        _test_double_fp32_values(-orig_d);
+}
+
+static void
+dix_double_fp_conversion(void)
+{
+    uint32_t i;
+    printf("Testing double to FP1616/FP3232 conversions\n");
+
+    _test_double_fp16_values(0);
+    for (i = 1; i < 0x7FFF; i <<= 1) {
+        double val;
+
+        val = i;
+        _test_double_fp16_values(val);
+        _test_double_fp32_values(val);
+
+        /* and some pseudo-random floating points */
+        val = i - 0.00382;
+        _test_double_fp16_values(val);
+        _test_double_fp32_values(val);
+
+        val = i + 0.00382;
+        _test_double_fp16_values(val);
+        _test_double_fp32_values(val);
+
+        val = i + 0.05234;
+        _test_double_fp16_values(val);
+        _test_double_fp32_values(val);
+
+        val = i + 0.12342;
+        _test_double_fp16_values(val);
+        _test_double_fp32_values(val);
+
+        val = i + 0.27583;
+        _test_double_fp16_values(val);
+        _test_double_fp32_values(val);
+
+        val = i + 0.50535;
+        _test_double_fp16_values(val);
+        _test_double_fp32_values(val);
+
+        val = i + 0.72342;
+        _test_double_fp16_values(val);
+        _test_double_fp32_values(val);
+
+        val = i + 0.80408;
+        _test_double_fp16_values(val);
+        _test_double_fp32_values(val);
+    }
+
+    for (i = 0x7FFFF; i < 0x7FFFFFFF; i <<= 1) {
+        _test_double_fp32_values(i);
+        /* and a few more random floating points, obtained
+         * by faceplanting into the numpad repeatedly */
+        _test_double_fp32_values(i + 0.010177);
+        _test_double_fp32_values(i + 0.213841);
+        _test_double_fp32_values(i + 0.348720);
+        _test_double_fp32_values(i + 0.472020);
+        _test_double_fp32_values(i + 0.572020);
+        _test_double_fp32_values(i + 0.892929);
+    }
+}
 
 int main(int argc, char** argv)
 {
+    dix_double_fp_conversion();
     dix_input_valuator_masks();
     dix_input_attributes();
     dix_init_valuators();
-- 
1.7.6.1




More information about the xorg-devel mailing list