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 }