1 //
2 // SPDX-License-Identifier: BSD-3-Clause
3 // Copyright Contributors to the OpenEXR Project.
4 //
5
6 // For the C version test, omit the lookup table, which validates that
7 // ``half.h`` works as a "header-only" implementation not requiring
8 // the compiled library. Note that the C-language support for half
9 // only includes conversion to and from float.
10 #define IMATH_HALF_NO_LOOKUP_TABLE
11
12 #include <half.h>
13 #include <math.h>
14 #include <stdio.h>
15 #include <string.h>
16
17 typedef union
18 {
19 uint32_t i;
20 float f;
21 } c_half_uif;
22
23 static const c_half_uif half_to_float[1 << 16] =
24 #include "../Imath/toFloat.h"
25
26 static const unsigned short half_eLut[1 << 9] =
27 #include "eLut.h"
28
exp_long_convert(int i)29 static short exp_long_convert (int i)
30 {
31 int s = (i >> 16) & 0x00008000;
32 int e = ((i >> 23) & 0x000000ff) - (127 - 15);
33 int m = i & 0x007fffff;
34
35 //fprintf( stderr, "old_convert: s %d e = %d, m = %d\n", s, e, m );
36 if (e <= 0)
37 {
38 if (e < -10)
39 {
40 return s;
41 }
42
43 m = m | 0x00800000;
44
45 int t = 14 - e;
46 int a = (1 << (t - 1)) - 1;
47 int b = (m >> t) & 1;
48
49 m = (m + a + b) >> t;
50
51 //fprintf( stderr, " <OLD> e %d, m: 0x%08X, t %d, a: %d 0x%08X, b: %d 0x%08X\n", e, m, t, a, a, b, b );
52 return s | m;
53 }
54 else if (e == 0xff - (127 - 15))
55 {
56 if (m == 0)
57 {
58 return s | 0x7c00;
59 }
60 else
61 {
62 m >>= 13;
63 return s | 0x7c00 | m | (m == 0);
64 }
65 }
66 else
67 {
68 m = m + 0x00000fff + ((m >> 13) & 1);
69
70 if (m & 0x00800000)
71 {
72 m = 0; // overflow in significand,
73 e += 1; // adjust exponent
74 }
75 if (e > 30)
76 {
77 return s | 0x7c00; // if this returns, the half becomes an
78 } // infinity with the same sign as f.
79 return s | (e << 10) | (m >> 13);
80 }
81 }
82
83 static uint16_t
exptable_method(float f)84 exptable_method (float f)
85 {
86 c_half_uif x;
87 uint16_t _h = 0;
88 x.f = f;
89
90 if (f == 0)
91 {
92 //
93 // Common special case - zero.
94 // Preserve the zero's sign bit.
95 //
96
97 _h = (x.i >> 16);
98 }
99 else
100 {
101 //
102 // We extract the combined sign and exponent, e, from our
103 // floating-point number, f. Then we convert e to the sign
104 // and exponent of the half number via a table lookup.
105 //
106 // For the most common case, where a normalized half is produced,
107 // the table lookup returns a non-zero value; in this case, all
108 // we have to do is round f's significand to 10 bits and combine
109 // the result with e.
110 //
111 // For all other cases (overflow, zeroes, denormalized numbers
112 // resulting from underflow, infinities and NANs), the table
113 // lookup returns zero, and we call a longer, non-inline function
114 // to do the float-to-half conversion.
115 //
116
117 int e = (x.i >> 23) & 0x000001ff;
118
119 e = half_eLut[e];
120
121 if (e)
122 {
123 //
124 // Simple case - round the significand, m, to 10
125 // bits and combine it with the sign and exponent.
126 //
127
128 int m = x.i & 0x007fffff;
129 _h = e + ((m + 0x00000fff + ((m >> 13) & 1)) >> 13);
130 }
131 else
132 {
133 //
134 // Difficult case - call a function.
135 //
136 _h = exp_long_convert (x.i);
137 }
138 }
139 return _h;
140 }
141
142 int
main(int argc,char * argv[])143 main (int argc, char* argv[])
144 {
145 int ret = 0;
146 c_half_uif conv;
147 half test, test2;
148 conv.f = HALF_DENORM_MIN + HALF_DENORM_MIN * 0.5f;
149 test = imath_float_to_half (conv.f);
150 test2 = exptable_method (conv.f);
151 if (test != test2)
152 {
153 fprintf (stderr,
154 "Invalid conversion of %.10g 0x%08X 0x%08X downconvert 0x%04X vs 0x%04X\n",
155 conv.f,
156 (conv.i >> 13) & 0x3ff,
157 (conv.i >> 13) & 0x3ff,
158 test,
159 test2);
160 ret = 1;
161 }
162
163 int diffcount = 0;
164 for (int i = 0; i < (1 << 16); ++i)
165 {
166 conv.f = imath_half_to_float ((half) i);
167 if (conv.i != half_to_float[i].i)
168 {
169 uint16_t h = (uint16_t) i;
170 uint16_t he = (h >> 10) & 0x1f;
171 uint16_t hm = (h & 0x3ff);
172
173 #ifdef __F16C__
174 // the intel instructions do something different w/ NaN values than the original half library
175 if (he == 0x1f && isnan (conv.f))
176 {
177 ++diffcount;
178 continue;
179 }
180 #endif
181 fprintf (
182 stderr,
183 "half to float %d: C gives %.10f (0x%08X) vs %.10f (0x%08X) [h 0x%04X he 0x%04X hm 0x%04X]\n",
184 i,
185 conv.f,
186 conv.i,
187 half_to_float[i].f,
188 half_to_float[i].i,
189 h,
190 he,
191 hm);
192 ret = 1;
193 }
194 }
195 if (diffcount != 0)
196 fprintf (
197 stderr,
198 "WARNING: Seems safe, but %d NaN values were different between hardware implementation and library implementation\n",
199 diffcount);
200
201 for (int i = 0; i < (1 << 16); ++i)
202 {
203 conv = half_to_float[i];
204 test = imath_float_to_half (conv.f);
205 if (test != i)
206 {
207 // well, we ensure that nan stays nan after conversion by
208 // adding a low bit, so it won't always align
209 int e = (conv.i >> 23) & 0xff;
210 int m = (conv.i & 0x007fffff);
211 if (e == 255 && m != 0)
212 continue;
213
214 fprintf (stderr,
215 "float to half %d: %.10f (0x%08X) gives %d 0x%04X (e is %d)\n",
216 i,
217 conv.f,
218 conv.i,
219 (int) test,
220 test,
221 e);
222 ret = 1;
223 }
224 }
225
226 return ret;
227 }
228