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