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 }