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