1 module gfm.math.half;
2 
3 import std.traits,
4        std.string;
5 
6 /** 
7 
8   16-bits floating point type (Half).
9   Implements conversion from ftp://www.fox-toolkit.org/pub/fasthalffloatconversion.pdf 
10   by Jeroen van der Zijp.
11 
12   Supports builtin operations that float support, but computations are performed in 32-bits
13   float and converted back.
14 
15   Bugs: rounding is not IEEE compliant.
16 
17  */ 
18 align(1) struct half
19 {
20     align(1):
21     public
22     {
23         ushort value;
24 
25         /// Construct a half from a float.
26         this(float n) pure nothrow
27         {
28             opAssign!float(n);
29         }
30 
31         /// Construct a half from another half.
32         this(half h) pure nothrow
33         {
34             opAssign!half(h);
35         }
36 
37         /// Converts to a pretty string.
38         string toString() const
39         {
40             return format("%s", value);
41         }
42 
43         /// Converts to a float.
44         float toFloat() pure const nothrow
45         {
46             return halfToFloat(value);
47         }
48 
49         /// Assign with float.
50         ref half opAssign(T)(T other) pure nothrow if (is(T: float))
51         {
52             value = floatToHalf(other);
53             return this;
54         }
55 
56         /// Assign with another half.
57         ref half opAssign(T)(T other) pure nothrow if (is(Unqual!T == half))
58         {
59             value = other.value;
60             return this;
61         }
62 
63         half opBinary(string op, T)(T o) pure const nothrow if (is(Unqual!T == half))
64         {
65             return opBinary!(op, float)(o.toFloat());
66         }
67 
68         half opBinary(string op, T)(T o) pure const nothrow if (is(T: float))
69         {
70             half res = void;
71             mixin("res.value = floatToHalf(toFloat() " ~ op ~ "o);");
72             return res;
73         }
74 
75         ref half opOpAssign(string op, T)(T o) pure nothrow
76         {
77             half res = opBinary!(op, T)(o);
78             this = res;
79             return this;
80         }
81 
82         half opUnary(string op)() pure const nothrow if (op == "+" || op == "-")
83         {
84             static if (op == "-")
85             {
86                 half h = this;
87                 h.value ^= 0x8000; // flip sign bit
88                 return h;
89             }
90             else static if (op == "+")
91                 return this;
92         }
93 
94 
95         bool opEquals(T)(T other) pure const if (!is(Unqual!T == half))
96         {
97             return this == half(other);
98         }
99 
100         bool opEquals(T)(T other) pure const if (is(Unqual!T == half))
101         {
102             return value == other.value;
103         }
104     }
105 }
106 
107 static assert (half.sizeof == 2);
108 
109 
110 // Conversions.
111 
112 private union uint_float
113 {
114     float f;
115     uint ui;
116 }
117 
118 /// Converts from float to half.
119 ushort floatToHalf(float f) pure nothrow
120 {
121     uint_float uf = void;
122     uf.f = f;
123     size_t idx = (uf.ui >> 23) & 0x1ff;
124     return cast(ushort)(basetable[idx] + ((uf.ui & 0x007fffff) >> shifttable[idx]));
125 }
126 
127 /// Converts from half to float.
128 float halfToFloat(ushort h) pure nothrow
129 {
130     uint_float uf = void;
131     uf.ui = mantissatable[offsettable[h>>10] + (h & 0x3ff)] + exponenttable[h>>10];
132     return uf.f;
133 }
134 
135 unittest
136 {
137     half a = 1.0f;
138     assert (a == 1);
139     half b = 2.0f;
140     assert (a * 2 == b);
141     half c = a + b;
142     half d = (b / a - c) ;
143     assert (-d == 1);
144 }
145 
146 private
147 {
148     // build tables through CTFE
149 
150     enum mantissatable =
151     (){
152         uint[2048] t;
153         t[0] = 0;
154         for (uint i = 1; i < 1024; ++i)
155         {
156             uint m = i << 13;            // zero pad mantissa bits
157             uint e = 0;                  // zero exponent
158             while(0 == (m & 0x00800000)) // while not normalized
159             {
160                 e -= 0x00800000;         // decrement exponent (1<<23)
161                 m = m << 1;              // shift mantissa
162             }
163 
164             m = m & (~0x00800000);       // clear leading 1 bit
165             e += 0x38800000;             // adjust bias ((127-14)<<23)
166             t[i] = m | e;                // return combined number
167         }
168 
169         for (uint i = 1024; i < 2047; ++i)
170             t[i] = 0x38000000 + ((i-1024) << 13);
171 
172         return t;
173     }();
174 
175     enum exponenttable =
176     (){
177         uint[64] t;
178         t[0] = 0;
179         for (uint i = 1; i <= 30; ++i)
180             t[i] = i << 23;
181         t[31] = 0x47800000;
182         t[32] = 0x80000000;
183         for (uint i = 33; i <= 62; ++i)
184             t[i] = 0x80000000 + ((i - 32) << 23);
185 
186         t[63] = 0xC7800000;
187         return t;
188     }();
189 
190     enum offsettable =
191     (){
192         ushort[64] t;
193         t[] = 1024;
194         t[0] = t[32] = 0;
195         return t;
196     }();
197 
198     enum basetable =
199     (){
200         ushort[512] t;
201         for (uint i = 0; i < 256; ++i)
202         {
203             int e = cast(int)i - 127;
204             if (e < -24)
205             {
206                 t[i | 0x000] = 0x0000;
207                 t[i | 0x100] = 0x8000;
208             }
209             else if(e < -14)
210             {
211                 t[i | 0x000] = (0x0400 >> (-e - 14));
212                 t[i | 0x100] = (0x0400 >> (-e - 14)) | 0x8000;
213             }
214             else if(e <= 15)
215             {
216                 t[i | 0x000] = cast(ushort)((e + 15) << 10);
217                 t[i | 0x100] = cast(ushort)((e + 15) << 10) | 0x8000;
218             }
219             else
220             {
221                 t[i | 0x000] = 0x7C00;
222                 t[i | 0x100] = 0xFC00;
223             }
224         }
225         return t;
226     }();
227 
228     enum shifttable =
229     (){
230         ubyte[512] t;
231 
232         for (uint i = 0; i < 256; ++i)
233         {
234             int e = cast(int)i - 127;
235             if (e < -24)
236             {
237                 t[i | 0x000] = 24;
238                 t[i | 0x100] = 24;
239             }
240             else if(e < -14)
241             {
242                 t[i | 0x000] = cast(ubyte)(-e - 1);
243                 t[i | 0x100] = cast(ubyte)(-e - 1);
244             }
245             else if(e <= 15)
246             {
247                 t[i | 0x000]=13;
248                 t[i | 0x100]=13;
249             }
250             else if (e < 128)
251             {
252                 t[i | 0x000]=24;
253                 t[i | 0x100]=24;
254             }
255             else
256             {
257                 t[i | 0x000] = 13;
258                 t[i | 0x100] = 13;
259             }
260         }
261 
262         return t;
263     }();
264 }