Use ryg's much faster fp16 conversion code.
[movit] / fp16.cpp
1 #include "fp16.h"
2
3 namespace movit {
4 namespace {
5
6 union fp32 {
7         float f;
8         unsigned int u;
9 };
10
11 }  // namespace
12
13 #ifndef __F16C__
14
15 float fp16_to_fp32(fp16_int_t h)
16 {
17         fp32 magic; magic.u = 113 << 23;
18         unsigned int shifted_exp = 0x7c00 << 13;  // exponent mask after shift
19         fp32 o;
20
21         // mantissa+exponent
22         unsigned int shifted = (h.val & 0x7fff) << 13;
23         unsigned int exponent = shifted & shifted_exp;
24
25         // exponent cases
26         o.u = shifted;
27         if (exponent == 0) {  // Zero / Denormal
28                 o.u += magic.u;
29                 o.f -= magic.f;
30         } else if (exponent == shifted_exp) {  // Inf/NaN
31                 o.u += (255 - 31) << 23;
32         } else {
33                 o.u += (127 - 15) << 23;
34         }
35
36         o.u |= (h.val & 0x8000) << 16;  // copy sign bit
37         return o.f;
38 }
39
40 fp16_int_t fp32_to_fp16(float x)
41 {
42         fp32 f; f.f = x;
43         unsigned int f32infty = 255 << 23;
44         unsigned int f16max = (127 + 16) << 23;
45         fp32 denorm_magic; denorm_magic.u = ((127 - 15) + (23 - 10) + 1) << 23;
46         unsigned int sign_mask = 0x80000000u;
47         fp16_int_t o = { 0 };
48
49         unsigned int sign = f.u & sign_mask;
50         f.u ^= sign;
51
52         // NOTE all the integer compares in this function can be safely
53         // compiled into signed compares since all operands are below
54         // 0x80000000. Important if you want fast straight SSE2 code
55         // (since there's no unsigned PCMPGTD).
56
57         if (f.u >= f16max) {  // result is Inf or NaN (all exponent bits set)
58                 o.val = (f.u > f32infty) ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf
59         } else {  // (De)normalized number or zero
60                 if (f.u < (113 << 23)) {  // resulting FP16 is subnormal or zero
61                         // use a magic value to align our 10 mantissa bits at the bottom of
62                         // the float. as long as FP addition is round-to-nearest-even this
63                         // just works.
64                         f.f += denorm_magic.f;
65
66                         // and one integer subtract of the bias later, we have our final float!
67                         o.val = f.u - denorm_magic.u;
68                 } else {
69                         unsigned int mant_odd = (f.u >> 13) & 1; // resulting mantissa is odd
70
71                         // update exponent, rounding bias part 1
72                         f.u += ((15 - 127) << 23) + 0xfff;
73                         // rounding bias part 2
74                         f.u += mant_odd;
75                         // take the bits!
76                         o.val = f.u >> 13;
77                 }
78         }
79
80         o.val |= sign >> 16;
81         return o;
82 }
83
84 #endif
85
86 }  // namespace