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