source: trunk/src/half.cpp @ 869

Last change on this file since 869 was 869, checked in by sam, 9 years ago

core: add a half to float conversion routine and utility floating point
functions such as isnan(), isinf() etc.

  • Property svn:keywords set to Id
File size: 4.5 KB
Line 
1//
2// Lol Engine
3//
4// Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net>
5//   This program is free software; you can redistribute it and/or
6//   modify it under the terms of the Do What The Fuck You Want To
7//   Public License, Version 2, as published by Sam Hocevar. See
8//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
9//
10
11#if defined HAVE_CONFIG_H
12#   include "config.h"
13#endif
14
15#include "core.h"
16
17using namespace std;
18
19namespace lol
20{
21
22/* Lookup table-based algorithm from “Fast Half Float Conversions”
23 * by Jeroen van der Zijp, November 2008. No rounding is performed,
24 * and some NaN values may be incorrectly converted to Inf. */
25half half::makefast(float f)
26{
27#define S4(x)    S1(4*(x)),  S1(4*(x)+1),  S1(4*(x)+2),  S1(4*(x)+3)
28#define S16(x)   S4(4*(x)),  S4(4*(x)+1),  S4(4*(x)+2),  S4(4*(x)+3)
29#define S64(x)  S16(4*(x)), S16(4*(x)+1), S16(4*(x)+2), S16(4*(x)+3)
30#define S256(x) S64(4*(x)), S64(4*(x)+1), S64(4*(x)+2), S64(4*(x)+3)
31
32    static uint16_t const basetable[512] =
33    {
34#define S1(i) (((i) < 103) ? 0x0000: \
35               ((i) < 113) ? 0x0400 >> (113 - (i)) : \
36               ((i) < 143) ? ((i) - 112) << 10 : 0x7c00)
37        S256(0),
38#undef S1
39#define S1(i) (0x8000 | (((i) < 103) ? 0x0000 : \
40                         ((i) < 113) ? 0x0400 >> (113 - (i)): \
41                         ((i) < 143) ? ((i) - 112) << 10 : 0x7c00))
42        S256(0),
43#undef S1
44    };
45
46    static uint8_t const shifttable[512] =
47    {
48#define S1(i) (((i) < 103) ? 24 : \
49               ((i) < 113) ? 126 - (i) : \
50               ((i) < 143 || (i) == 255) ? 13 : 24)
51        S256(0), S256(0),
52#undef S1
53    };
54
55    union { float f; uint32_t x; } u = { f };
56
57    uint16_t bits = basetable[(u.x >> 23) & 0x1ff];
58    bits |= (u.x & 0x007fffff) >> shifttable[(u.x >> 23) & 0x1ff];
59    return makebits(bits);
60}
61
62/* This method is faster than the OpenEXR implementation (very often
63 * used, eg. in Ogre), with the additional benefit of rounding, inspired
64 * by James Tursa’s half-precision code. */
65half half::makeslow(float f)
66{
67    union { float f; uint32_t x; } u = { f };
68
69    uint16_t bits = (u.x >> 16) & 0x8000; /* Get the sign */
70    uint16_t m = (u.x >> 12) & 0x07ff; /* Keep one extra bit for rounding */
71    unsigned int e = (u.x >> 23) & 0xff; /* Using int is faster here */
72
73    /* If zero, or denormal, or exponent underflows too much for a denormal,
74     * return signed zero. */
75    if (e < 103)
76        return makebits(bits);
77
78    /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */
79    if (e > 142)
80    {
81        bits |= 0x7c00u;
82        /* If exponent was 0xff and one mantissa bit was set, it means NaN,
83         * not Inf, so make sure we set one mantissa bit too. */
84        bits |= e == 255 && (u.x & 0x007fffffu);
85        return makebits(bits);
86    }
87
88    /* If exponent underflows but not too much, return a denormal */
89    if (e < 113)
90    {
91        m |= 0x0800u;
92        /* Extra rounding may overflow and set mantissa to 0 and exponent
93         * to 1, which is OK. */
94        bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1);
95        return makebits(bits);
96    }
97
98    bits |= ((e - 112) << 10) | (m >> 1);
99    /* Extra rounding. An overflow will set mantissa to 0 and increment
100     * the exponent, which is OK. */
101    bits += m & 1;
102    return makebits(bits);
103}
104
105half::operator float() const
106{
107    union { float f; uint32_t x; } u;
108
109    uint32_t s = (m_bits & 0x8000u) << 16;
110
111    if ((m_bits & 0x7fffu) == 0)
112    {
113        u.x = (uint32_t)m_bits << 16;
114        return u.f;
115    }
116
117    uint32_t e = m_bits & 0x7c00u;
118    uint32_t m = m_bits & 0x03ffu;
119
120    if (e == 0)
121    {
122        static int const shifttable[32] =
123        {
124            10, 1, 9, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 7, 0,
125            2, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0, 5, 6, 0,
126        };
127
128        uint32_t v = m | (m >> 1);
129        v |= v >> 2;
130        v |= v >> 4;
131        v |= v >> 8;
132
133        e = shifttable[(v * 0x07C4ACDDU) >> 27];
134        m <<= e;
135
136        /* We don't have to remove the 10th mantissa bit because it gets
137         * added to our underestimated exponent. */
138        u.x = s | (((112 - e) << 23) + (m << 13));
139        return u.f;
140    }
141
142    if (e == 0x7c00u)
143    {
144        /* The amd64 pipeline likes the if() better than a ternary operator
145         * or any other trick I could find. --sam */
146        if (m == 0)
147            u.x = s | 0x7f800000u;
148        else
149            u.x = s | 0x7fc00000u;
150
151        return u.f;
152    }
153
154    u.x = s | (((e >> 10) + 112) << 23) | (m << 13);
155
156    return u.f;
157}
158
159} /* namespace lol */
160
Note: See TracBrowser for help on using the repository browser.