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