1 // Written in the D programming language 2 3 /** 4 * Arithmetic context for floating-point decimal arithmetic. 5 * 6 * An implementation of the 7 * General Decimal Arithmetic Specification. 8 * 9 * Authors: Paul D. Anderson 10 * 11 * Copyright: Copyright 2009-2016 by Paul D. Anderson. 12 * 13 * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a> 14 * 15 * Standards: Conforms to the 16 * General Decimal Arithmetic Specification, 17 * Version 1.70, (25 March 2009). 18 */ 19 20 module eris.decimal.context; 21 22 unittest { 23 writeln(" context tests "); 24 writeln("=========================="); 25 } 26 27 version(unittest) { 28 import std.stdio; 29 } 30 31 /// Pre-defined default context for decimal numbers. 32 public enum Context DefaultContext = Context(99, 9999, HALF_EVEN); 33 34 /// Approximates floating-point real numbers. 35 public enum Context RealContext = Context(real.dig, real.max_10_exp, HALF_EVEN); 36 /// Approximates floating-point double numbers. 37 public enum Context DoubleContext = Context(double.dig, double.max_10_exp, HALF_EVEN); 38 /// Approximates floating-point float numbers. 39 public enum Context FloatContext = Context(float.dig, float.max_10_exp, HALF_EVEN); 40 41 /// Decimal64 BID 42 public enum Context Bid32 = Context(7, 96, HALF_EVEN); 43 /// Decimal64 BID 44 public enum Context Bid64 = Context(16, 369, HALF_EVEN); 45 46 /** 47 * The available rounding modes. For cumulative operations use the 48 * halfEven mode to prevent accumulation of errors. Otherwise, the 49 * halfUp and halfDown modes are satisfactory. The up, down, floor, 50 * and ceiling modes are also useful for some operations. 51 * General Decimal Arithmetic Specification, p. 13-14. 52 * 53 */ 54 public enum Round { 55 none, 56 halfEven, 57 halfDown, 58 halfUp, 59 down, 60 up, 61 floor, 62 ceiling, 63 } 64 65 alias ROUND_NONE = Round.none; 66 alias HALF_EVEN = Round.halfEven; 67 alias HALF_DOWN = Round.halfDown; 68 alias HALF_UP = Round.halfUp; 69 alias ROUND_DOWN = Round.down; 70 alias ROUND_UP = Round.up; 71 alias FLOOR = Round.floor; 72 alias CEILING = Round.ceiling; 73 74 /** 75 * Flags: The available flags and trap-enablers. 76 * 77 * The larger value have higher precedence. 78 * If more than one flag is set by an operation and traps are enabled, 79 * the flag with higher precedence will throw its exception. 80 * General Decimal Arithmetic Specification, p. 15. 81 */ 82 83 /// 84 public enum : ubyte 85 { 86 INVALID_OPERATION = 0x80, 87 DIVISION_BY_ZERO = 0x40, 88 OVERFLOW = 0x20, 89 SUBNORMAL = 0x10, 90 INEXACT = 0x08, 91 ROUNDED = 0x04, 92 UNDERFLOW = 0x02, 93 CLAMPED = 0x01 94 } 95 96 97 /** 98 * Decimal arithmetic operations are governed by their context. 99 * The context specifies the precision (number of decimal digits) 100 * the maximum exponent value and the rounding mode in place for the operation. 101 * The result of most operations will be rounded to the type precision 102 * using the context rounding mode 103 */ 104 public struct Context 105 { 106 public immutable int precision; 107 public immutable int maxExpo; 108 public immutable Round mode; 109 110 @disable this(); 111 112 this(int precision, int maxExpo, Round mode) 113 { 114 this.precision = precision; 115 this.maxExpo = maxExpo; 116 this.mode = mode; 117 } 118 } 119 120 //public bool enableFlags = false; 121 122 version(unittest) 123 { 124 public enum Context TestContext = Context(9, 99, HALF_EVEN); 125 } 126 127 /** 128 * Standards: "The exceptional conditions are grouped into signals, 129 * which can be controlled individually. 130 * The context contains a flag (which is either 0 or 1) 131 * and a trap-enabler (which also is either 0 or 1) for each signal. 132 * For each of the signals, the corresponding flag is 133 * set to 1 when the signal occurs. 134 * It is only reset to 0 by explicit user action." 135 * General Decimal Arithmetic Specification, p. 15. 136 */ 137 public struct ContextFlags { 138 139 private static ubyte flags = 0; 140 private static ubyte traps = 0; 141 142 // static ContextFlags instance = ContextFlags(); 143 144 /// Sets or resets the specified context flag(s). 145 @safe 146 public void set(ubyte flags, bool value = true) { 147 if (value) { 148 ubyte saved = this.flags; 149 this.flags |= flags; 150 ubyte changed = saved ^ flags; 151 checkFlags(changed); 152 } else { 153 this.flags &= !flags; 154 } 155 } 156 157 public void resetFlags(ubyte flags) { 158 set(flags, false); 159 } 160 161 // Checks the state of the flags. If a flag is set and its 162 // trap-enabler is set, an exception is thrown. 163 @safe 164 public void checkFlags(ubyte flags) { 165 if (flags & INVALID_OPERATION && traps & INVALID_OPERATION) { 166 throw new InvalidOperationException("InvalidOperation"); 167 } 168 if (flags & DIVISION_BY_ZERO && traps & DIVISION_BY_ZERO) { 169 throw new DivByZeroException("DivisionByZero"); 170 } 171 if (flags & OVERFLOW && traps & OVERFLOW) { 172 throw new OverflowException("Overflow"); 173 } 174 if (flags & SUBNORMAL && traps & SUBNORMAL) { 175 throw new SubnormalException("Subnormal"); 176 } 177 if (flags & INEXACT && traps & INEXACT) { 178 throw new InexactException("Inexact"); 179 } 180 if (flags & ROUNDED && traps & ROUNDED) { 181 throw new RoundedException("Rounded"); 182 } 183 if (flags & UNDERFLOW && traps & UNDERFLOW) { 184 throw new UnderflowException("Underflow"); 185 } 186 if (flags & CLAMPED && traps & CLAMPED) { 187 throw new ClampedException("Clamped"); 188 } 189 } 190 191 /// Gets the value of the specified context flag. 192 public bool getFlags(ubyte flags) { 193 return (this.flags & flags) == flags; 194 } 195 196 /// Returns a snapshot of the context flags. 197 public ubyte getFlags() { 198 return flags; 199 } 200 201 /// Clears all the context flags. 202 public void clearFlags() { 203 flags = 0; 204 } 205 206 /// Sets or resets the specified trap(s). 207 public void setTraps(ubyte traps, bool value = true) { 208 if (value) { 209 this.traps |= traps; 210 } else { 211 this.traps &= !traps; 212 } 213 } 214 215 /// Returns the value of the specified trap. 216 bool getTrap(ubyte trap) { 217 return (this.traps & trap) == trap; 218 } 219 220 /// Returns a snapshot of traps. 221 public ubyte getTraps() { 222 return traps; 223 } 224 225 /// Clears all the traps. 226 public void clearTraps() { 227 traps = 0; 228 } 229 230 }; 231 232 // this is the single instance of the context flags. 233 public enum ContextFlags contextFlags = ContextFlags(); 234 235 //-------------------------- 236 // Context flags and trap-enablers 237 //-------------------------- 238 /** 239 * The base class for all decimal arithmetic exceptions. 240 */ 241 @safe 242 class DecimalException: object.Exception { 243 this(string msg, string file = __FILE__, 244 uint line = cast(uint)__LINE__, Throwable next = null) { 245 super(msg, file, line, next); 246 } 247 }; 248 249 /** 250 * Raised when the exponent of a result has been altered or constrained 251 * in order to fit the constraints of a specific concrete representation. 252 * General Decimal Arithmetic Specification, p. 15. 253 */ 254 @safe 255 class ClampedException: DecimalException { 256 this(string msg, string file = __FILE__, 257 uint line = cast(uint)__LINE__, Throwable next = null) { 258 super(msg, file, line, next); 259 } 260 }; 261 262 /** 263 * Raised when a non-zero dividend is divided by zero. 264 * General Decimal Arithmetic Specification, p. 15. 265 */ 266 @safe 267 class DivByZeroException: DecimalException { 268 this(string msg, string file = __FILE__, 269 uint line = cast(uint)__LINE__, Throwable next = null) { 270 super(msg, file, line, next); 271 } 272 }; 273 274 /** 275 * Raised when a result is not exact (one or more non-zero coefficient 276 * digits were discarded during rounding). 277 * General Decimal Arithmetic Specification, p. 15. 278 */ 279 @safe 280 class InexactException: DecimalException { 281 this(string msg, string file = __FILE__, 282 uint line = cast(uint)__LINE__, Throwable next = null) { 283 super(msg, file, line, next); 284 } 285 }; 286 287 /** 288 * Raised when a result would be undefined or impossible. 289 * General Decimal Arithmetic Specification, p. 15. 290 */ 291 @safe 292 class InvalidOperationException: DecimalException { 293 this(string msg, string file = __FILE__, 294 uint line = cast(uint)__LINE__, Throwable next = null) { 295 super(msg, file, line, next); 296 } 297 }; 298 299 /** 300 * Raised when the exponent of a result is too large to be represented. 301 * General Decimal Arithmetic Specification, p. 15. 302 */ 303 @safe 304 class OverflowException: DecimalException { 305 this(string msg, string file = __FILE__, 306 uint line = cast(uint)__LINE__, Throwable next = null) { 307 super(msg, file, line, next); 308 } 309 }; 310 311 /** 312 * Raised when a result has been rounded (that is, some zero or non-zero 313 * coefficient digits were discarded). 314 * General Decimal Arithmetic Specification, p. 15. 315 */ 316 @safe 317 class RoundedException: DecimalException { 318 this(string msg, string file = __FILE__, 319 uint line = cast(uint)__LINE__, Throwable next = null) { 320 super(msg, file, line, next); 321 } 322 }; 323 324 /** 325 * Raised when a result is subnormal (its adjusted exponent is less 326 * than the minimum exponent) before any rounding. 327 * General Decimal Arithmetic Specification, p. 15. 328 */ 329 @safe 330 class SubnormalException: DecimalException { 331 this(string msg, string file = __FILE__, 332 uint line = cast(uint)__LINE__, Throwable next = null) { 333 super(msg, file, line, next); 334 } 335 }; 336 337 /** 338 * Raised when a result is both subnormal and inexact. 339 * General Decimal Arithmetic Specification, p. 15. 340 */ 341 @safe 342 class UnderflowException: DecimalException { 343 this(string msg, string file = __FILE__, 344 uint line = cast(uint)__LINE__, Throwable next = null) { 345 super(msg, file, line, next); 346 } 347 }; 348 349 unittest 350 { 351 writeln("=========================="); 352 } 353