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