dix: add utility functions for double to/fro FP1616/FP3232 conversion

Co-authored-by: Jeremy Huddleston <jeremyhu@apple.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Jeremy Huddleston <jeremyhu@apple.com>
Reviewed-by: Mark Kettenis <kettenis@openbsd.org>
This commit is contained in:
Jeremy Huddleston 2011-10-05 15:02:52 -07:00
parent 09dbfcb0ad
commit f9c6903d4a
3 changed files with 197 additions and 0 deletions

View File

@ -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;
}

View File

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

View File

@ -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();