Add support for half floats.

iqm and OpenGL use them, so they might come in handy. The tests use values
from wikipedia and a couple extra.
This commit is contained in:
Bill Currie 2012-04-26 20:55:11 +09:00
parent e06ee34287
commit 8791b35e55
4 changed files with 133 additions and 1 deletions

View file

@ -449,6 +449,8 @@ void Mat4Mult (const mat4_t a, const mat4_t b, mat4_t c);
VectorNegate ((sp)->normal, (dp)->normal); \
} while (0)
int16_t FloatToHalf (float x);
float HalfToFloat (int16_t x);
extern plane_t * const frustum;
extern inline qboolean R_CullBox (const vec3_t mins, const vec3_t maxs);
extern inline qboolean R_CullSphere (const vec3_t origin, const float radius);

View file

@ -54,6 +54,80 @@ VISIBLE const vec_t * const quat_origin = _quat_origin;
#define DEG2RAD(a) (a * (M_PI / 180.0))
#define FMANTBITS 23
#define FMANTMASK ((1 << FMANTBITS) - 1)
#define FEXPBITS 8
#define FEXPMASK ((1 << FEXPBITS) - 1)
#define FBIAS (1 << (FEXPBITS - 1))
#define FEXPMAX ((1 << FEXPBITS) - 1)
#define HMANTBITS 10
#define HMANTMASK ((1 << HMANTBITS) - 1)
#define HEXPBITS 5
#define HEXPMASK ((1 << HEXPBITS) - 1)
#define HBIAS (1 << (HEXPBITS - 1))
#define HEXPMAX ((1 << HEXPBITS) - 1)
int16_t
FloatToHalf (float x)
{
union {
float f;
uint32_t u;
} uf;
unsigned sign;
int exp;
unsigned mant;
int16_t half;
uf.f = x;
sign = (uf.u >> (FEXPBITS + FMANTBITS)) & 1;
exp = ((uf.u >> FMANTBITS) & FEXPMASK) - FBIAS + HBIAS;
mant = (uf.u & FMANTMASK) >> (FMANTBITS - HMANTBITS);
if (exp <= 0) {
mant |= 1 << HMANTBITS;
mant >>= min (1 - exp, HMANTBITS + 1);
exp = 0;
} else if (exp >= HEXPMAX) {
mant = 0;
exp = HEXPMAX;
}
half = (sign << (HEXPBITS + HMANTBITS)) | (exp << HMANTBITS) | mant;
return half;
}
float
HalfToFloat (int16_t x)
{
union {
float f;
uint32_t u;
} uf;
unsigned sign;
int exp;
unsigned mant;
sign = (x >> (HEXPBITS + HMANTBITS)) & 1;
exp = ((x >> HMANTBITS) & HEXPMASK);
mant = (x & HMANTMASK) << (FMANTBITS - HMANTBITS);
if (exp == 0) {
if (mant) {
while (mant < (1 << FMANTBITS)) {
mant <<= 1;
exp--;
}
mant &= (1 << FMANTBITS) - 1;
exp += FBIAS - HBIAS + 1;
}
} else if (exp == HEXPMAX) {
exp = FEXPMAX;
} else {
exp += FBIAS - HBIAS;
}
uf.u = (sign << (FEXPBITS + FMANTBITS)) | (exp << FMANTBITS) | mant;
return uf.f;
}
static void
ProjectPointOnPlane (vec3_t dst, const vec3_t p, const vec3_t normal)

View file

@ -2,7 +2,11 @@ AUTOMAKE_OPTIONS= foreign
INCLUDES= -I$(top_srcdir)/include
check_PROGRAMS=test-qfs test-quat test-vrect
check_PROGRAMS=test-half test-qfs test-quat test-vrect
test_half_SOURCES=test-half.c
test_half_LDADD=$(top_builddir)/libs/util/libQFutil.la
test_half_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la
test_qfs_SOURCES=test-qfs.c
test_qfs_LDADD=$(top_builddir)/libs/util/libQFutil.la

View file

@ -0,0 +1,52 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include "QF/mathlib.h"
struct {
int16_t half;
float flt;
} tests [] = {
{0x3c00, 1.0f},
{0x3c01, 1.0009765625f},
{0xc000, -2.0f},
{0x7bff, 65504.0f},
{0x0400, 6.103515625e-05f},
{0x03ff, 6.0975551605224609375e-05f},
{0x0001, 5.9604644775390625e-08f},
{0x0000, 0.0f},
{0x8000, -0.0f},
#if __GNUC_PREREQ(3,3)
{0x7c00, __builtin_huge_val ()},
{0xfc00, -__builtin_huge_val ()},
#endif
{0x3555, 0.333251953125f},
{0x3e00, 1.5f},
{0x0000, 0.0f},
};
#define num_tests (sizeof (tests) / sizeof (tests[0]))
int
main (int argc, const char **argv)
{
size_t i;
int res = 0;
for (i = 0; i < num_tests; i++) {
float flt;
int16_t half;
flt = HalfToFloat (tests[i].half);
half = FloatToHalf (tests[i].flt);
if (flt != tests[i].flt || half != tests[i].half) {
res |= 1;
printf ("test %d failed\n", (int) i);
printf ("expect: %4x %.20g\n", tests[i].half, tests[i].flt);
printf ("got : %4x %.20g\n", half, flt);
}
}
return res;
}