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