Mercurial > projects > decimal
annotate src/decimal/decimal.d @ 1:a984d3056cc4
Incorporated Boost License
author | Paul (paul.d.anderson@comcast.net) |
---|---|
date | Sat, 13 Mar 2010 18:51:22 -0800 |
parents | 42cf4db6be32 |
children | bfda0b347c07 |
rev | line source |
---|---|
1
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
1 /** |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
2 * A D programming language implementation of the |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
3 * General Decimal Arithmetic Specification, |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
4 * Version 1.70, (25 March 2009). |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
5 * |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
6 * by Paul D. Anderson |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
7 * |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
8 * Boost Software License - Version 1.0 - August 17th, 2003 |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
9 * |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
10 * Permission is hereby granted, free of charge, to any person or organization |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
11 * obtaining a copy of the software and accompanying documentation covered by |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
12 * this license (the "Software") to use, reproduce, display, distribute, |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
13 * execute, and transmit the Software, and to prepare derivative works of the |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
14 * Software, and to permit third-parties to whom the Software is furnished to |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
15 * do so, all subject to the following: |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
16 * |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
17 * The copyright notices in the Software and this entire statement, including |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
18 * the above license grant, this restriction and the following disclaimer, |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
19 * must be included in all copies of the Software, in whole or in part, and |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
20 * all derivative works of the Software, unless such copies or derivative |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
21 * works are solely in the form of machine-executable object code generated by |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
22 * a source language processor. |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
23 * |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
26 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
27 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
28 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
29 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
30 * DEALINGS IN THE SOFTWARE. |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
31 **/ |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
32 |
a984d3056cc4
Incorporated Boost License
Paul (paul.d.anderson@comcast.net)
parents:
0
diff
changeset
|
33 // TODO: ensure context flags are being set and cleared properly. |
0 | 34 |
35 // TODO: add exp() add sqrt() add power(): | |
36 | |
37 // TODO: add a set/getPayload to Decimal | |
38 | |
39 // TODO: unittest opPostDec && opPostInc. | |
40 | |
41 // TODO: this(str): add tests for just over/under int.max, int.min | |
42 | |
43 // TODO: organize by structs, lib functions modules functions, etc. | |
44 | |
45 // TODO: opEquals unit test should include numerically equal testing. | |
46 | |
47 // TODO: write some test cases for flag setting. test the add/sub/mul/div functions | |
48 | |
49 // TODO: to/from real or double (float) values needs definition and implementation. | |
50 | |
51 // TODO: need to determine the property name for dig and/or digits | |
52 | |
53 module decimal.decimal; | |
54 | |
55 import decimal.context; | |
56 import std.bigint; | |
57 import std.conv; | |
58 import std.ctype: isdigit; | |
59 import std.math: PI, LOG2; | |
60 import std.stdio: write, writeln; | |
61 import std.string; | |
62 | |
63 // special values | |
64 private enum SpVal {CLEAR, ZERO, INF, QNAN, SNAN}; | |
65 | |
66 // common decimal "numbers" | |
67 public: | |
68 static immutable Decimal ONE = {spval:SpVal.CLEAR, ceff:{[1]}}; | |
69 static immutable Decimal TEN = {spval:SpVal.CLEAR, ceff:{[10]}}; | |
70 static immutable Decimal NaN = {spval:SpVal.QNAN}; | |
71 static immutable Decimal POS_INF = {spval:SpVal.INF}; | |
72 static immutable Decimal NEG_INF = {sign:true, SpVal.INF}; | |
73 static immutable Decimal ZERO = {spval:SpVal.ZERO}; | |
74 static immutable Decimal NEG_ZERO = {sign:true, SpVal.ZERO}; | |
75 | |
76 // active context | |
77 public DecimalContext context = DEFAULT_CONTEXT; | |
78 | |
79 // singleton ContextStack | |
80 private ContextStack contextStack; | |
81 | |
82 /// saves the current context | |
83 public void pushContext() { | |
84 contextStack.push(); | |
85 } | |
86 | |
87 /// restores the previous context | |
88 public void popContext() { | |
89 contextStack.pop(); | |
90 } | |
91 | |
92 /** | |
93 * A struct representing an arbitrary-precision floating-point number. | |
94 * | |
95 * The implementation follows the General Decimal Arithmetic | |
96 * Specification, Version 1.70 (25 Mar 2009), | |
97 * http://www.speleotrove.com/decimal. This specification conforms with | |
98 * IEEE standard 754-2008. | |
99 */ | |
100 struct Decimal { | |
101 | |
102 private: | |
103 SpVal spval = SpVal.QNAN; // special values: default value is quiet NaN | |
104 bool sign = false; // true if the value is negative, false otherwise. | |
105 int expo = 0; // the exponent of the Decimal value | |
106 BigInt ceff; // the coefficient of the Decimal value | |
107 // NOTE: not a uint -- causes math problems down the line. | |
108 int digits; // the number of decimal digits in this number. | |
109 // (unless the number is a special value) | |
110 | |
111 //-------------------------------- | |
112 // construction | |
113 //-------------------------------- | |
114 | |
115 public: | |
116 /** | |
117 * Constructs a Decimal number from a sign, a BigInt coefficient and | |
118 * an integer exponent. | |
119 * The precision of the number is deduced from the number of decimal | |
120 * digits in the coefficient. | |
121 */ | |
122 this(const bool sign, const BigInt coefficient, const int exponent) { | |
123 this.clear(); | |
124 if (coefficient < BIG_ZERO) { | |
125 this.sign = !sign; | |
126 this.ceff = -coefficient; | |
127 } | |
128 else { | |
129 this.sign = sign; | |
130 this.ceff = coefficient; | |
131 if (coefficient == BIG_ZERO) { | |
132 this.spval = SpVal.ZERO; | |
133 } | |
134 } | |
135 this.expo = exponent; | |
136 this.digits = numDigits(this.ceff, 1); | |
137 } | |
138 | |
139 /** | |
140 * Constructs a Decimal number from a sign, an integer coefficient and | |
141 * an integer exponent. | |
142 */ | |
143 this(const bool sign, const int coefficient, const int exponent) { | |
144 this(sign, BigInt(coefficient), exponent); | |
145 } | |
146 | |
147 /** | |
148 * Constructs a Decimal number from a sign, a special value string and | |
149 * an optional payload. | |
150 */ | |
151 this(const bool sign, string str, const uint payload = 0) { | |
152 this.clear();; | |
153 this.sign = sign; | |
154 if (icmp(str, "inf") == 0 || icmp(str, "infinity") == 0) { | |
155 spval = SpVal.INF; | |
156 return; | |
157 } | |
158 if (icmp(str, "snan") == 0) { | |
159 spval = SpVal.SNAN; | |
160 } | |
161 else { | |
162 spval = SpVal.QNAN; | |
163 } | |
164 ceff = payload; | |
165 } | |
166 | |
167 /** | |
168 * Constructs a Decimal from a BigInt coefficient and an int exponent. | |
169 * The sign of the number is the sign of the coefficient. | |
170 */ | |
171 this(const BigInt coefficient, const int exponent) { | |
172 this(false, coefficient, exponent); | |
173 }; | |
174 | |
175 /** | |
176 * Constructs a Decimal from a BigInt. | |
177 */ | |
178 this(const BigInt coefficient) { | |
179 this(coefficient, 0); | |
180 }; | |
181 | |
182 /** | |
183 * Constructs a Decimal from an integer coefficient and an integer exponent. | |
184 */ | |
185 this(const long coefficient, const int exponent) { | |
186 this(BigInt(coefficient), exponent); | |
187 } | |
188 | |
189 /** | |
190 * Constructs a Decimal from an integer value. | |
191 */ | |
192 this(const long coefficient) { | |
193 this(BigInt(coefficient), 0); | |
194 } | |
195 | |
196 /** | |
197 * Constructs a Decimal from an integer coefficient, exponent and precision. | |
198 */ | |
199 // TODO: should this set flags? probably not. | |
200 this(const long coefficient, const int exponent, const int precision) { | |
201 this(coefficient, exponent); | |
202 pushContext(); | |
203 context.precision = precision; | |
204 setDigits(this); | |
205 popContext(); | |
206 } | |
207 | |
208 /** | |
209 * Constructs a Decimal from a real value. | |
210 */ | |
211 this(const real r) { | |
212 string str = format("%.*G", cast(int)context.precision, r); | |
213 this = str; | |
214 } | |
215 | |
216 /** | |
217 * Constructs a Decimal from a double value. | |
218 * Set to the specified precision | |
219 */ | |
220 this(const real r, int precision) { | |
221 string str = format("%.*E", precision, r); | |
222 this = str; | |
223 } | |
224 | |
225 // copy constructor | |
226 this(const Decimal that) { | |
227 this = that; | |
228 }; | |
229 | |
230 // construct from string representation | |
231 this(const string str) { | |
232 this = str; | |
233 }; | |
234 | |
235 unittest { | |
236 write("construction."); | |
237 Decimal f = Decimal(BigInt(1234), 567); | |
238 assert(f.toString() == "1.234E+570"); | |
239 f = Decimal(BigInt(1234)); | |
240 assert(f.toString() == "1234"); | |
241 f = Decimal(BigInt(123400)); | |
242 assert(f.toString() == "123400"); | |
243 f = Decimal(1234, 567); | |
244 assert(f.toString() == "1.234E+570"); | |
245 f = Decimal(BigInt(1234)); | |
246 assert(f.toString() == "1234"); | |
247 f = Decimal(1234, 0, 9); | |
248 assert(f.toString() == "1234.00000"); | |
249 Decimal dec = Decimal(1234, 1, 9); | |
250 assert(dec.toString() == "12340.0000"); | |
251 dec = Decimal(12, 1, 9); | |
252 assert(dec.toString() == "120.000000"); | |
253 dec = Decimal(int.max, -4, 9); | |
254 assert(dec.toString() == "214748.365"); | |
255 dec = Decimal(int.max, -4); | |
256 assert(dec.toString() == "214748.3647"); | |
257 dec = Decimal(1234567, -2, 5); | |
258 assert(dec.toString() == "12346"); | |
259 writeln("passed"); | |
260 } | |
261 | |
262 unittest { | |
263 write("this(str)...."); | |
264 Decimal f; | |
265 string str = "0"; | |
266 f = str; | |
267 assert(f.toString() == str); | |
268 assert(f.toAbstract() == "[0,0,0]"); | |
269 str = "0.00"; | |
270 f = str; | |
271 assert(f.toString() == str); | |
272 assert(f.toAbstract() == "[0,0,-2]"); | |
273 str = "0.0"; | |
274 f = str; | |
275 assert(f.toString() == str); | |
276 assert(f.toAbstract() == "[0,0,-1]"); | |
277 f = "0."; | |
278 assert(f.toString() == "0"); | |
279 assert(f.toAbstract() == "[0,0,0]"); | |
280 f = ".0"; | |
281 assert(f.toString() == "0.0"); | |
282 assert(f.toAbstract() == "[0,0,-1]"); | |
283 str = "1.0"; | |
284 f = str; | |
285 assert(f.toString() == str); | |
286 assert(f.toAbstract() == "[0,10,-1]"); | |
287 str = "1."; | |
288 f = str; | |
289 assert(f.toString() == "1"); | |
290 assert(f.toAbstract() == "[0,1,0]"); | |
291 str = ".1"; | |
292 f = str; | |
293 assert(f.toString() == "0.1"); | |
294 assert(f.toAbstract() == "[0,1,-1]"); | |
295 f = Decimal("123"); | |
296 assert(f.toString() == "123"); | |
297 f = Decimal("-123"); | |
298 assert(f.toString() == "-123"); | |
299 f = Decimal("1.23E3"); | |
300 assert(f.toString() == "1.23E+3"); | |
301 f = Decimal("1.23E"); | |
302 assert(f.toString() == "NaN"); | |
303 f = Decimal("1.23E-"); | |
304 assert(f.toString() == "NaN"); | |
305 f = Decimal("1.23E+"); | |
306 assert(f.toString() == "NaN"); | |
307 f = Decimal("1.23E+3"); | |
308 assert(f.toString() == "1.23E+3"); | |
309 f = Decimal("1.23E3B"); | |
310 assert(f.toString() == "NaN"); | |
311 f = Decimal("12.3E+007"); | |
312 assert(f.toString() == "1.23E+8"); | |
313 f = Decimal("12.3E+70000000000"); | |
314 assert(f.toString() == "NaN"); | |
315 f = Decimal("12.3E+7000000000"); | |
316 assert(f.toString() == "NaN"); | |
317 f = Decimal("12.3E+700000000"); | |
318 // writeln(f.toString()); | |
319 assert(f.toString() == "1.23E+700000001"); | |
320 f = Decimal("12.3E-700000000"); | |
321 // writeln(f.toString()); | |
322 assert(f.toString() == "1.23E-699999999"); | |
323 // NOTE: since there will still be adjustments -- maybe limit to 99999999? | |
324 f = Decimal("12.0"); | |
325 assert(f.toString() == "12.0"); | |
326 f = Decimal("12.3"); | |
327 assert(f.toString() == "12.3"); | |
328 f = Decimal("1.23E-3"); | |
329 assert(f.toString() == "0.00123"); | |
330 f = Decimal("0.00123"); | |
331 assert(f.toString() == "0.00123"); | |
332 f = Decimal("-1.23E-12"); | |
333 assert(f.toString() == "-1.23E-12"); | |
334 f = Decimal("-0"); | |
335 assert(f.toString() == "-0"); | |
336 f = Decimal("inf"); | |
337 assert(f.toString() == "Infinity"); | |
338 f = Decimal("NaN"); | |
339 assert(f.toString() == "NaN"); | |
340 f = Decimal("-NaN"); | |
341 assert(f.toString() == "-NaN"); | |
342 f = Decimal("sNaN"); | |
343 assert(f.toString() == "sNaN"); | |
344 f = Decimal("Fred"); | |
345 assert(f.toString() == "NaN"); | |
346 writeln("passed"); | |
347 } | |
348 | |
349 /** | |
350 * dup property | |
351 */ | |
352 const Decimal dup() { | |
353 Decimal cp; | |
354 cp.sign = sign; | |
355 cp.spval = spval; | |
356 cp.ceff = ceff; | |
357 cp.expo = expo; | |
358 return cp; | |
359 } | |
360 | |
361 //-------------------------------- | |
362 // assignment | |
363 //-------------------------------- | |
364 | |
365 /// Assigns a Decimal (makes a copy) | |
366 void opAssign(const Decimal that) { | |
367 this.sign = that.sign; | |
368 this.spval = that.spval; | |
369 this.digits = that.digits; | |
370 this.expo = that.expo; | |
371 this.ceff = that.ceff; | |
372 } | |
373 | |
374 /// Assigns a floating point value. | |
375 void opAssign(const real r) { | |
376 this = Decimal(r); | |
377 } | |
378 | |
379 /// Assign an integer value | |
380 void opAssign(const long n) { | |
381 spval = (n == 0) ? SpVal.ZERO : SpVal.CLEAR; | |
382 sign = n < 0; | |
383 ceff = sign ? -n : n; | |
384 expo = 0; | |
385 digits = numDigits(ceff, 1); | |
386 } | |
387 | |
388 /// Assign a BigInt | |
389 void opAssign(const BigInt big) { | |
390 spval = (big == BIG_ZERO) ? SpVal.ZERO : SpVal.CLEAR; | |
391 sign = big < 0; | |
392 ceff = sign ? -big : big; | |
393 expo = 0; | |
394 digits = numDigits(ceff, 1); | |
395 } | |
396 | |
397 /// Assigns a string | |
398 void opAssign(const string numeric_string) { | |
399 clear(); | |
400 sign = false; | |
401 | |
402 // strip, copy, tolower | |
403 char[] str = strip(numeric_string).dup; | |
404 tolowerInPlace(str); | |
405 | |
406 // get sign, if any | |
407 if (startsWith(str,"-")) { | |
408 sign = true; | |
409 str = str[1..$]; | |
410 } | |
411 else if (startsWith(str,"+")) { | |
412 str = str[1..$]; | |
413 } | |
414 | |
415 // check for NaN | |
416 if (startsWith(str,"nan")) { | |
417 spval = SpVal.QNAN; | |
418 if (str == "nan") { | |
419 ceff = BIG_ZERO; | |
420 return; | |
421 } | |
422 // set payload | |
423 str = str[3..$]; | |
424 // ensure string is all digits | |
425 foreach(char c; str) { | |
426 if (!isdigit(c)) { | |
427 return; | |
428 } | |
429 } | |
430 // convert string to payload | |
431 ceff = BigInt(str.idup); | |
432 return; | |
433 }; | |
434 | |
435 // check for sNaN | |
436 if (startsWith(str,"snan")) { | |
437 spval = SpVal.SNAN; | |
438 if (str == "snan") { | |
439 ceff = BIG_ZERO; | |
440 return; | |
441 } | |
442 // set payload | |
443 str = str[4..$]; | |
444 // ensure string is all digits | |
445 foreach(char c; str) { | |
446 if (!isdigit(c)) { | |
447 return; | |
448 } | |
449 } | |
450 // convert string to payload | |
451 ceff = BigInt(str.idup); | |
452 return; | |
453 }; | |
454 | |
455 // check for infinity | |
456 if (str == "inf" || str == "infinity") { | |
457 spval = SpVal.INF; | |
458 return; | |
459 }; | |
460 | |
461 clear(); | |
462 // check for exponent | |
463 int pos = find(str, 'e'); | |
464 if (pos > 0) { | |
465 // if it's just a trailing 'e', return NaN | |
466 if (pos == str.length - 1) { | |
467 spval = SpVal.QNAN; | |
468 return; | |
469 } | |
470 // split the string into coefficient and exponent | |
471 char[] xstr = str[pos+1..$]; | |
472 str = str[0..pos]; | |
473 // assume exponent is positive | |
474 bool xneg = false; | |
475 // check for minus sign | |
476 if (startsWith(xstr, "-")) { | |
477 xneg = true; | |
478 xstr = xstr[1..$]; | |
479 } | |
480 // check for plus sign | |
481 else if (startsWith(xstr, "+")) { | |
482 xstr = xstr[1..$]; | |
483 } | |
484 | |
485 // ensure it's not now empty | |
486 if (xstr.length < 1) { | |
487 spval = SpVal.QNAN; | |
488 return; | |
489 } | |
490 | |
491 // ensure exponent is all digits | |
492 foreach(char c; xstr) { | |
493 if (!isdigit(c)) { | |
494 spval = SpVal.QNAN; | |
495 return; | |
496 } | |
497 } | |
498 | |
499 // trim leading zeros | |
500 while (xstr[0] == '0' && xstr.length > 1) { | |
501 xstr = xstr[1..$]; | |
502 } | |
503 | |
504 // make sure it will fit into an int | |
505 if (xstr.length > 10) { | |
506 spval = SpVal.QNAN; | |
507 return; | |
508 } | |
509 if (xstr.length == 10) { | |
510 // try to convert it to a long (should work) and | |
511 // then see if the long value is too big (or small) | |
512 long lex = to!long(xstr); | |
513 if ((xneg && (-lex < int.min)) || lex > int.max) { | |
514 spval = SpVal.QNAN; | |
515 return; | |
516 } | |
517 expo = cast(int) lex; | |
518 } | |
519 else { | |
520 // everything should be copacetic at this point | |
521 expo = to!int(xstr); | |
522 } | |
523 if (xneg) { | |
524 expo = -expo; | |
525 } | |
526 } | |
527 else { | |
528 expo = 0; | |
529 } | |
530 | |
531 // remove trailing decimal point | |
532 if (endsWith(str, ".")) { | |
533 str = str[0..$-1]; | |
534 } | |
535 // strip leading zeros | |
536 while (str[0] == '0' && str.length > 1) { | |
537 str = str[1..$]; | |
538 } | |
539 | |
540 // remove internal decimal point | |
541 int point = find(str, '.'); | |
542 if (point >= 0) { | |
543 // excise the point and adjust exponent | |
544 str = str[0..point] ~ str[point+1..$]; | |
545 int diff = str.length - point; | |
546 expo -= diff; | |
547 } | |
548 | |
549 // ensure string is not empty | |
550 if (str.length < 1) { | |
551 spval = SpVal.QNAN; | |
552 return; | |
553 } | |
554 | |
555 // ensure string is all digits | |
556 foreach(char c; str) { | |
557 if (!isdigit(c)) { | |
558 spval = SpVal.QNAN; | |
559 return; | |
560 } | |
561 } | |
562 // convert string to BigInt | |
563 ceff = BigInt(str.idup); | |
564 digits = numDigits(ceff, str.length); | |
565 if (ceff == BIG_ZERO) spval = SpVal.ZERO; | |
566 | |
567 }; // end opAssign(string) | |
568 | |
569 //-------------------------------- | |
570 // string representations | |
571 //-------------------------------- | |
572 | |
573 /** | |
574 * Converts a Decimal to an abstract string representation. | |
575 */ | |
576 private const string toAbstract() { | |
577 switch (spval) { | |
578 case SpVal.SNAN: | |
579 string payload = ceff == BIG_ZERO ? "" : "," ~ ceff.toString(); | |
580 return format("[%d,%s%s]", sign ? 1 : 0, "sNaN", payload); | |
581 case SpVal.QNAN: | |
582 string payload = ceff == BIG_ZERO ? "" : "," ~ ceff.toString(); | |
583 return format("[%d,%s%s]", sign ? 1 : 0, "qNaN", payload); | |
584 case SpVal.INF: | |
585 return format("[%d,%s]", sign ? 1 : 0, "inf"); | |
586 default: | |
587 return format("[%d,%s,%d]", sign ? 1 : 0, ceff.toString(), expo); | |
588 } | |
589 } | |
590 | |
591 /** | |
592 * Converts a Decimal to a string representation. | |
593 */ | |
594 const string toString() { | |
595 | |
596 // string representation of special values | |
597 if (spval > SpVal.ZERO) { | |
598 string str; | |
599 switch(spval) { | |
600 case SpVal.ZERO: | |
601 str = "0"; | |
602 break; | |
603 case SpVal.INF: | |
604 str = "Infinity"; | |
605 break; | |
606 case SpVal.SNAN: | |
607 str = "sNaN"; | |
608 break; | |
609 default: | |
610 str = "NaN"; | |
611 } | |
612 if (spval >= SpVal.QNAN && ceff != BIG_ZERO) { | |
613 str ~= ceff.toString(); | |
614 } | |
615 return sign ? "-" ~ str : str; | |
616 } | |
617 | |
618 // string representation of finite numbers | |
619 string cstr = ceff.toString(); | |
620 int clen = cstr.length; | |
621 int adjx = expo + clen - 1; | |
622 | |
623 // if exponent is small, don't use exponential notation | |
624 if (expo <= 0 && adjx >= -6) { | |
625 // if exponent is not zero, insert a decimal point | |
626 if (expo != 0) { | |
627 int point = std.math.abs(expo); | |
628 // if coefficient is too small, pad with zeroes | |
629 if (point > clen) { | |
630 cstr = zfill(cstr, point); | |
631 clen = cstr.length; | |
632 } | |
633 // if no chars precede the decimal point, prefix a zero | |
634 if (point == clen) { | |
635 cstr = "0." ~ cstr; | |
636 } | |
637 // otherwise insert a decimal point | |
638 else { | |
639 cstr = insert(cstr, cstr.length - point, "."); | |
640 } | |
641 } | |
642 return sign ? "-" ~ cstr : cstr; | |
643 } | |
644 // use exponential notation | |
645 // else { | |
646 if (clen > 1) { | |
647 cstr = insert(cstr, 1, "."); | |
648 } | |
649 string xstr = to!string(adjx); | |
650 if (adjx >= 0) { | |
651 xstr = "+" ~ xstr; | |
652 } | |
653 string str = cstr ~ "E" ~ xstr; | |
654 if (sign) { | |
655 return "-" ~ str; | |
656 } | |
657 else { | |
658 return str; | |
659 } | |
660 // } // end else | |
661 | |
662 }; // end toString() | |
663 | |
664 /** | |
665 * Converts a Decimal to an engineering string representation. | |
666 */ | |
667 const string toEngString() { | |
668 return toString(); | |
669 }; | |
670 | |
671 /** | |
672 * Converts a Decimal to a scientific string representation. | |
673 */ | |
674 const string toSciString() { | |
675 return toString(); | |
676 }; | |
677 | |
678 unittest { | |
679 write("to-sci-str..."); | |
680 Decimal dec = Decimal(false, 123, 0); | |
681 assert(dec.toString() == "123"); | |
682 assert(dec.toAbstract() == "[0,123,0]"); | |
683 dec = Decimal(true, 123, 0); | |
684 assert(dec.toString() == "-123"); | |
685 assert(dec.toAbstract() == "[1,123,0]"); | |
686 dec = Decimal(false, 123, 1); | |
687 assert(dec.toString() == "1.23E+3"); | |
688 assert(dec.toAbstract() == "[0,123,1]"); | |
689 dec = Decimal(false, 123, 3); | |
690 assert(dec.toString() == "1.23E+5"); | |
691 assert(dec.toAbstract() == "[0,123,3]"); | |
692 dec = Decimal(false, 123, -1); | |
693 assert(dec.toString() == "12.3"); | |
694 assert(dec.toAbstract() == "[0,123,-1]"); | |
695 dec = Decimal(false, 123, -5); | |
696 assert(dec.toString() == "0.00123"); | |
697 assert(dec.toAbstract() == "[0,123,-5]"); | |
698 dec = Decimal(false, 123, -10); | |
699 assert(dec.toString() == "1.23E-8"); | |
700 assert(dec.toAbstract() == "[0,123,-10]"); | |
701 dec = Decimal(true, 123, -12); | |
702 assert(dec.toString() == "-1.23E-10"); | |
703 assert(dec.toAbstract() == "[1,123,-12]"); | |
704 dec = Decimal(false, 0, 0); | |
705 assert(dec.toString() == "0"); | |
706 assert(dec.toAbstract() == "[0,0,0]"); | |
707 dec = Decimal(false, 0, -2); | |
708 assert(dec.toString() == "0.00"); | |
709 assert(dec.toAbstract() == "[0,0,-2]"); | |
710 dec = Decimal(false, 0, 2); | |
711 assert(dec.toString() == "0E+2"); | |
712 assert(dec.toAbstract() == "[0,0,2]"); | |
713 dec = Decimal(true, 0, 0); | |
714 assert(dec.toString() == "-0"); | |
715 assert(dec.toAbstract() == "[1,0,0]"); | |
716 dec = Decimal(false, 5, -6); | |
717 assert(dec.toString() == "0.000005"); | |
718 assert(dec.toAbstract() == "[0,5,-6]"); | |
719 dec = Decimal(false, 50,-7); | |
720 assert(dec.toString() == "0.0000050"); | |
721 assert(dec.toAbstract() == "[0,50,-7]"); | |
722 dec = Decimal(false, 5, -7); | |
723 assert(dec.toString() == "5E-7"); | |
724 assert(dec.toAbstract() == "[0,5,-7]"); | |
725 dec = Decimal(false, "inf"); | |
726 assert(dec.toString() == "Infinity"); | |
727 assert(dec.toAbstract() == "[0,inf]"); | |
728 dec = Decimal(true, "inf"); | |
729 assert(dec.toString() == "-Infinity"); | |
730 assert(dec.toAbstract() == "[1,inf]"); | |
731 dec = Decimal(false, "NaN"); | |
732 assert(dec.toString() == "NaN"); | |
733 assert(dec.toAbstract() == "[0,qNaN]"); | |
734 dec = Decimal(false, "NaN", 123); | |
735 assert(dec.toString() == "NaN123"); | |
736 assert(dec.toAbstract() == "[0,qNaN,123]"); | |
737 dec = Decimal(true, "sNaN"); | |
738 assert(dec.toString() == "-sNaN"); | |
739 assert(dec.toAbstract() == "[1,sNaN]"); | |
740 writeln("passed"); | |
741 } | |
742 | |
743 | |
744 //-------------------------------- | |
745 // member properties | |
746 //-------------------------------- | |
747 | |
748 /// returns the exponent of this Decimal | |
749 const int exponent() { | |
750 return this.expo; | |
751 } | |
752 /// returns the coefficient of this Decimal | |
753 const BigInt coefficient() { | |
754 return this.ceff; | |
755 } | |
756 | |
757 /// returns the sign of this Decimal | |
758 const int sgn() { | |
759 if (isZero) return 0; | |
760 return sign ? -1 : 1; | |
761 } | |
762 | |
763 /// returns a Decimal with the same exponent as this Decimal | |
764 /// and a coefficient of 1. | |
765 const Decimal quantum() { | |
766 return Decimal(1, this.expo); | |
767 } | |
768 | |
769 //-------------------------------- | |
770 // floating point properties | |
771 //-------------------------------- | |
772 | |
773 static int precision() { | |
774 return context.precision; | |
775 } | |
776 | |
777 /// returns the default value for this type (NaN) | |
778 static Decimal init() { | |
779 return NaN; | |
780 } | |
781 | |
782 /// Returns NaN | |
783 static Decimal nan() { | |
784 return NaN; | |
785 } | |
786 | |
787 /// Returns positive infinity. | |
788 static Decimal infinity() { | |
789 return POS_INF; | |
790 } | |
791 | |
792 //-------------------------------- | |
793 // classification properties | |
794 //-------------------------------- | |
795 | |
796 /** | |
797 * Returns true if this number is canonical representation (always true). | |
798 */ | |
799 const bool isCanonical() { | |
800 return true; | |
801 } | |
802 | |
803 /** | |
804 * Returns true if this Decimal is +/- zero. | |
805 */ | |
806 const bool isZero() { | |
807 return spval == SpVal.ZERO; | |
808 } | |
809 | |
810 /** | |
811 * Returns true if this Decimal is a quiet or signaling NaN. | |
812 */ | |
813 const bool isNaN() { | |
814 return this.spval == SpVal.QNAN || this.spval == SpVal.SNAN; | |
815 } | |
816 | |
817 /** | |
818 * Returns true if this Decimal is a signaling NaN. | |
819 */ | |
820 const bool isSignaling() { | |
821 return this.spval == SpVal.SNAN; | |
822 } | |
823 | |
824 /** | |
825 * Returns true if this Decimal is a quiet NaN. | |
826 */ | |
827 const bool isQuiet() { | |
828 return this.spval == SpVal.QNAN; | |
829 } | |
830 | |
831 /** | |
832 * Returns true if this Decimal is +/- infinity. | |
833 */ | |
834 const bool isInfinite() { | |
835 return this.spval == SpVal.INF; | |
836 } | |
837 | |
838 /** | |
839 * Returns true if this Decimal is not +/- infinity and not a NaN. | |
840 */ | |
841 const bool isFinite() { | |
842 return spval != SpVal.INF | |
843 && spval != SpVal.QNAN | |
844 && spval != SpVal.SNAN; | |
845 } | |
846 | |
847 /** | |
848 * Returns true if this Decimal is a NaN or infinity. | |
849 */ | |
850 const bool isSpecial() { | |
851 return spval == SpVal.INF | |
852 || spval == SpVal.QNAN | |
853 || spval == SpVal.SNAN; | |
854 } | |
855 | |
856 /** | |
857 * Returns true if this Decimal is negative. (Includes -0) | |
858 */ | |
859 const bool isSigned() { | |
860 return this.sign; | |
861 } | |
862 | |
863 /** | |
864 * Returns true if this Decimal is subnormal. | |
865 */ | |
866 const bool isSubnormal() { | |
867 if (spval != SpVal.CLEAR) return false; | |
868 return adjustedExponent < context.eMin; | |
869 } | |
870 | |
871 /** | |
872 * Returns true if this Decimal is subnormal. | |
873 */ | |
874 const bool isNormal() { | |
875 if (spval != SpVal.CLEAR) return false; | |
876 return adjustedExponent >= context.eMin; | |
877 } | |
878 | |
879 //-------------------------------- | |
880 // comparison | |
881 //-------------------------------- | |
882 | |
883 /** | |
884 * Returns -1, 0 or 1, if this number is less than, equal to or | |
885 * greater than the argument, respectively. | |
886 */ | |
887 int opCmp(const Decimal that) { | |
888 return icompare(this, that); | |
889 } | |
890 | |
891 /** | |
892 * Returns true if this Decimal is equal to the specified Decimal. | |
893 * A NaN is not equal to any number, not even to another NaN. | |
894 * Infinities are equal if they have the same sign. | |
895 * Zeros are equal regardless of sign. | |
896 * Finite numbers are equal if they are numerically equal to the current precision. | |
897 * A Decimal may not be equal to itself (this != this) if it is a NaN. | |
898 */ | |
899 const bool opEquals(ref const Decimal that) { | |
900 // if either is NaN... | |
901 if (this.isNaN || that.isNaN) return false; | |
902 | |
903 // if either is infinite... | |
904 if (this.isInfinite || that.isInfinite) { | |
905 return (this.spval == that.spval && this.sign == that.sign); | |
906 } | |
907 | |
908 // if either is zero... | |
909 if (this.isZero || that.isZero) { | |
910 return (this.isZero && that.isZero); | |
911 } | |
912 // if their signs differ | |
913 if (this.sign != that.sign) { | |
914 return false; | |
915 } | |
916 | |
917 // if they have the same representation, they are equal | |
918 if (this.expo == that.expo && this.ceff == that.ceff) { | |
919 return true; | |
920 } | |
921 | |
922 // otherwise they are equal if they represent the same value | |
923 // NOTE: this is only a check to current precision. | |
924 Decimal result = this - that; | |
925 return result.isZero; | |
926 } | |
927 | |
928 unittest { | |
929 write("equals......."); | |
930 Decimal op1; | |
931 Decimal op2; | |
932 op1 = "NaN"; | |
933 op2 = "NaN"; | |
934 assert(op1 != op2); | |
935 op1 = "inf"; | |
936 op2 = "inf"; | |
937 assert(op1 == op2); | |
938 op2 = "-inf"; | |
939 assert(op1 != op2); | |
940 op1 = "-inf"; | |
941 assert(op1 == op2); | |
942 op2 = "NaN"; | |
943 assert(op1 != op2); | |
944 op1 = 0; | |
945 assert(op1 != op2); | |
946 op2 = 0; | |
947 assert(op1 == op2); | |
948 writeln("passed"); | |
949 } | |
950 | |
951 //-------------------------------- | |
952 // unary arithmetic operators | |
953 //-------------------------------- | |
954 | |
955 /** | |
956 * unary minus -- returns a copy with the opposite sign. | |
957 * This operation may set flags -- equivalent to | |
958 * subtract('0', b); | |
959 */ | |
960 const Decimal opNeg() { | |
961 return minus(this); | |
962 } | |
963 | |
964 /** | |
965 * unary plus -- returns a copy. | |
966 * This operation may set flags -- equivalent to | |
967 * add('0', a); | |
968 */ | |
969 const Decimal opPos() { | |
970 return plus(this); | |
971 } | |
972 | |
973 /** | |
974 * Returns this + 1. | |
975 */ | |
976 Decimal opPostInc() { | |
977 this += ONE; | |
978 return this; | |
979 } | |
980 | |
981 /** | |
982 * Returns this - 1. | |
983 */ | |
984 Decimal opPostDec() { | |
985 this -= ONE; | |
986 return this; | |
987 } | |
988 | |
989 //-------------------------------- | |
990 // binary arithmetic operators | |
991 //-------------------------------- | |
992 | |
993 /// Adds a Decimal to this and returns the Decimal result | |
994 const Decimal opAdd(T:Decimal)(const T addend) { | |
995 return add(this, addend); | |
996 } | |
997 | |
998 // Adds a number to this and returns the result. | |
999 const Decimal opAdd(T)(const T addend) { | |
1000 return add(this, Decimal(addend), context); | |
1001 } | |
1002 | |
1003 const Decimal opSub(T:Decimal)(const T subtrahend) { | |
1004 return subtract(this, subtrahend); | |
1005 } | |
1006 | |
1007 const Decimal opSub(T)(const T subtrahend) { | |
1008 return subtract(this, Decimal(subtrahend), context); | |
1009 } | |
1010 | |
1011 const Decimal opMul(T:Decimal)(const T factor) { | |
1012 return multiply(this, factor); | |
1013 } | |
1014 | |
1015 const Decimal opMul(T)(const T factor) { | |
1016 return multiply(this, Decimal(factor)); | |
1017 } | |
1018 | |
1019 const Decimal opDiv(T:Decimal)(const T divisor) { | |
1020 return divide(this, divisor); | |
1021 } | |
1022 | |
1023 const Decimal opDiv(T)(const T divisor) { | |
1024 return divide(this, Decimal(divisor)); | |
1025 } | |
1026 | |
1027 const Decimal opMod(T:Decimal)(const T divisor) { | |
1028 return remainder(this, divisor); | |
1029 } | |
1030 | |
1031 const Decimal opMod(T)(const T divisor) { | |
1032 return remainder(this, Decimal(divisor)); | |
1033 } | |
1034 | |
1035 //-------------------------------- | |
1036 // arithmetic assignment operators | |
1037 //-------------------------------- | |
1038 | |
1039 Decimal opAddAssign(T)(const T addend) { | |
1040 this = this + addend; | |
1041 return this; | |
1042 } | |
1043 | |
1044 Decimal opSubAssign(T)(const T subtrahend) { | |
1045 this = this - subtrahend; | |
1046 return this; | |
1047 } | |
1048 | |
1049 Decimal opMulAssign(T)(const T factor) { | |
1050 this = this * factor; | |
1051 return this; | |
1052 } | |
1053 | |
1054 Decimal opDivAssign(T)(const T divisor) { | |
1055 this = this / divisor; | |
1056 return this; | |
1057 } | |
1058 | |
1059 Decimal opModAssign(T)(const T divisor) { | |
1060 this = this % divisor; | |
1061 return this; | |
1062 } | |
1063 | |
1064 //----------------------------- | |
1065 // nextUp, nextDown, nextAfter | |
1066 //----------------------------- | |
1067 | |
1068 const Decimal nextUp() { | |
1069 return nextPlus(this); | |
1070 } | |
1071 | |
1072 const Decimal nextDown() { | |
1073 return nextMinus(this); | |
1074 } | |
1075 | |
1076 const Decimal nextAfter(const Decimal dcm) { | |
1077 return nextToward(this, dcm); | |
1078 } | |
1079 | |
1080 private: | |
1081 /** | |
1082 * clears the special value flags | |
1083 */ | |
1084 void clear() { | |
1085 spval = SpVal.CLEAR; | |
1086 } | |
1087 | |
1088 const int adjustedExponent() { | |
1089 return expo + digits - 1; | |
1090 } | |
1091 | |
1092 const bool overflow() { | |
1093 return adjustedExponent > context.eMax; | |
1094 } | |
1095 | |
1096 unittest{ | |
1097 write("overflow....."); | |
1098 Decimal dec = Decimal(123, 99); | |
1099 assert(dec.overflow); | |
1100 dec = Decimal(12, 99); | |
1101 assert(dec.overflow); | |
1102 dec = Decimal(1, 99); | |
1103 assert(!dec.overflow); | |
1104 dec = Decimal(9, 99); | |
1105 assert(!dec.overflow); | |
1106 writeln("passed"); | |
1107 } | |
1108 | |
1109 } // end struct Decimal | |
1110 | |
1111 | |
1112 //-------------------------------- | |
1113 // context functions | |
1114 //-------------------------------- | |
1115 | |
1116 // TODO: this is actually a property of the context. | |
1117 | |
1118 /** | |
1119 * Returns radix of this representation (10). | |
1120 */ | |
1121 public int radix() { | |
1122 return 10; | |
1123 } | |
1124 | |
1125 unittest { | |
1126 write("radix........"); | |
1127 assert(radix() == 10); | |
1128 writeln("passed"); | |
1129 } | |
1130 | |
1131 //-------------------------------- | |
1132 // classification functions | |
1133 //-------------------------------- | |
1134 | |
1135 public string classify(const Decimal dcm) { | |
1136 if (dcm.isSignaling()) { | |
1137 return "sNaN"; | |
1138 } | |
1139 if (dcm.isQuiet) { | |
1140 return "NaN"; | |
1141 } | |
1142 if (dcm.isInfinite) { | |
1143 return dcm.sign ? "-Infinity" : "+Infinity"; | |
1144 } | |
1145 if (dcm.isSubnormal) { | |
1146 return dcm.sign ? "-Subnormal" : "+Subnormal"; | |
1147 } | |
1148 if (dcm.isZero) { | |
1149 return dcm.sign ? "-Zero" : "+Zero"; | |
1150 } | |
1151 return dcm.sign ? "-Normal" : "+Normal"; | |
1152 } | |
1153 | |
1154 /** | |
1155 * Returns true if this number is canonical representation (always true). | |
1156 */ | |
1157 public bool isCanonical(const Decimal dcm) { | |
1158 return dcm.isCanonical; | |
1159 } | |
1160 | |
1161 /** | |
1162 * Returns true if this Decimal is a signaling NaN. | |
1163 */ | |
1164 public bool isSignaling(const Decimal dcm) { | |
1165 return dcm.isSignaling; | |
1166 } | |
1167 | |
1168 /** | |
1169 * Returns true if the specified Decimal is a quiet NaN. | |
1170 */ | |
1171 public bool isQuiet(const Decimal dcm) { | |
1172 return dcm.isQuiet; | |
1173 } | |
1174 | |
1175 public bool isFinite(const Decimal dcm) { | |
1176 return dcm.isFinite; | |
1177 } | |
1178 | |
1179 public bool isInfinite(const Decimal dcm) { | |
1180 return dcm.isInfinite; | |
1181 } | |
1182 | |
1183 public bool isNaN(const Decimal dcm) { | |
1184 return dcm.isNaN; | |
1185 } | |
1186 | |
1187 public bool isNormal(const Decimal dcm) { | |
1188 return dcm.isNormal; | |
1189 } | |
1190 | |
1191 public bool isSubnormal(const Decimal dcm) { | |
1192 return dcm.isSubnormal; | |
1193 } | |
1194 | |
1195 public bool isZero(const Decimal dcm) { | |
1196 return dcm.isZero; | |
1197 } | |
1198 | |
1199 public bool isSigned(const Decimal dcm) { | |
1200 return dcm.isSigned; | |
1201 } | |
1202 | |
1203 unittest { | |
1204 write("classify....."); | |
1205 Decimal dcm; | |
1206 dcm = "Infinity"; | |
1207 assert(classify(dcm) == "+Infinity"); | |
1208 dcm = "1E-10"; | |
1209 assert(classify(dcm) == "+Normal"); | |
1210 dcm = "2.50"; | |
1211 assert(classify(dcm) == "+Normal"); | |
1212 dcm = "0.1E-99"; | |
1213 assert(classify(dcm) == "+Subnormal"); | |
1214 dcm = "0"; | |
1215 assert(classify(dcm) == "+Zero"); | |
1216 dcm = "-0"; | |
1217 assert(classify(dcm) == "-Zero"); | |
1218 dcm = "-0.1E-99"; | |
1219 assert(classify(dcm) == "-Subnormal"); | |
1220 dcm = "-1E-10"; | |
1221 assert(classify(dcm) == "-Normal"); | |
1222 dcm = "-2.50"; | |
1223 assert(classify(dcm) == "-Normal"); | |
1224 dcm = "-Infinity"; | |
1225 assert(classify(dcm) == "-Infinity"); | |
1226 dcm = "NaN"; | |
1227 assert(classify(dcm) == "NaN"); | |
1228 dcm = "-NaN"; | |
1229 assert(classify(dcm) == "NaN"); | |
1230 dcm = "sNaN"; | |
1231 assert(classify(dcm) == "sNaN"); | |
1232 | |
1233 dcm = Decimal("2.50"); | |
1234 assert(isCanonical(dcm)); | |
1235 | |
1236 dcm = Decimal("2.50"); | |
1237 assert(isFinite(dcm)); | |
1238 dcm = Decimal("-0.3"); | |
1239 assert(isFinite(dcm)); | |
1240 dcm = 0; | |
1241 assert(isFinite(dcm)); | |
1242 dcm = Decimal("Inf"); | |
1243 assert(!isFinite(dcm)); | |
1244 dcm = Decimal("-Inf"); | |
1245 assert(!isFinite(dcm)); | |
1246 dcm = Decimal("NaN"); | |
1247 assert(!isFinite(dcm)); | |
1248 | |
1249 dcm = Decimal("2.50"); | |
1250 assert(!isInfinite(dcm)); | |
1251 dcm = Decimal("-Inf"); | |
1252 assert(isInfinite(dcm)); | |
1253 dcm = Decimal("NaN"); | |
1254 assert(!isInfinite(dcm)); | |
1255 | |
1256 dcm = Decimal("2.50"); | |
1257 assert(!isNaN(dcm)); | |
1258 dcm = Decimal("NaN"); | |
1259 assert(isNaN(dcm)); | |
1260 dcm = Decimal("-sNaN"); | |
1261 assert(isNaN(dcm)); | |
1262 | |
1263 dcm = Decimal("2.50"); | |
1264 assert(isNormal(dcm)); | |
1265 dcm = Decimal("0.1E-99"); | |
1266 assert(!isNormal(dcm)); | |
1267 dcm = Decimal("0.00"); | |
1268 assert(!isNormal(dcm)); | |
1269 dcm = Decimal("-Inf"); | |
1270 assert(!isNormal(dcm)); | |
1271 dcm = Decimal("NaN"); | |
1272 assert(!isNormal(dcm)); | |
1273 writeln("passed"); | |
1274 | |
1275 dcm = Decimal("2.50"); | |
1276 assert(!isQuiet(dcm)); | |
1277 dcm = Decimal("NaN"); | |
1278 assert(isQuiet(dcm)); | |
1279 dcm = Decimal("sNaN"); | |
1280 assert(!isQuiet(dcm)); | |
1281 | |
1282 dcm = Decimal("2.50"); | |
1283 assert(!isSignaling(dcm)); | |
1284 dcm = Decimal("NaN"); | |
1285 assert(!isSignaling(dcm)); | |
1286 dcm = Decimal("sNaN"); | |
1287 assert(isSignaling(dcm)); | |
1288 | |
1289 dcm = Decimal("2.50"); | |
1290 assert(!isSigned(dcm)); | |
1291 dcm = Decimal("-12"); | |
1292 assert(isSigned(dcm)); | |
1293 dcm = Decimal("-0"); | |
1294 assert(isSigned(dcm)); | |
1295 | |
1296 dcm = Decimal("2.50"); | |
1297 assert(!isSubnormal(dcm)); | |
1298 dcm = Decimal("0.1E-99"); | |
1299 assert(isSubnormal(dcm)); | |
1300 dcm = Decimal("0.00"); | |
1301 assert(!isSubnormal(dcm)); | |
1302 dcm = Decimal("-Inf"); | |
1303 assert(!isSubnormal(dcm)); | |
1304 dcm = Decimal("NaN"); | |
1305 assert(!isSubnormal(dcm)); | |
1306 | |
1307 dcm = Decimal("0"); | |
1308 assert(isZero(dcm)); | |
1309 dcm = Decimal("2.50"); | |
1310 assert(!isZero(dcm)); | |
1311 dcm = Decimal("-0E+2"); | |
1312 assert(isZero(dcm)); | |
1313 | |
1314 } | |
1315 | |
1316 //-------------------------------- | |
1317 // copy functions | |
1318 //-------------------------------- | |
1319 | |
1320 /** | |
1321 * Returns a copy of the operand. | |
1322 * The copy is unaffected by context; no flags are changed. | |
1323 */ | |
1324 Decimal copy(const Decimal dcm) { | |
1325 Decimal cpy = dcm; | |
1326 return cpy; | |
1327 } | |
1328 | |
1329 /** | |
1330 * Returns a copy of the operand with a positive sign. | |
1331 * The copy is unaffected by context; no flags are changed. | |
1332 */ | |
1333 Decimal copyAbs(const Decimal dcm) { | |
1334 Decimal cpy = dcm; | |
1335 cpy.sign = false; | |
1336 return cpy; | |
1337 } | |
1338 | |
1339 /** | |
1340 * Returns a copy of the operand with the sign inverted. | |
1341 * The copy is unaffected by context; no flags are changed. | |
1342 */ | |
1343 Decimal copyNegate(const Decimal dcm) { | |
1344 Decimal cpy = dcm; | |
1345 cpy.sign = !dcm.sign; | |
1346 return cpy; | |
1347 } | |
1348 | |
1349 /** | |
1350 * Returns a copy of the first operand with the sign of the second operand. | |
1351 * The copy is unaffected by context; no flags are changed. | |
1352 */ | |
1353 Decimal copySign(const Decimal dcm1, const Decimal dcm2) { | |
1354 Decimal cpy = dcm1; | |
1355 cpy.sign = dcm2.sign; | |
1356 return cpy; | |
1357 } | |
1358 | |
1359 // TODO: these should actually be compare-total assertions | |
1360 // This is probably true of other unit tests as well | |
1361 unittest { | |
1362 write("copy........."); | |
1363 Decimal dcm; | |
1364 Decimal expd; | |
1365 dcm = "2.1"; | |
1366 expd = "2.1"; | |
1367 assert(copy(dcm) == expd); | |
1368 dcm = "-1.00"; | |
1369 expd = "-1.00"; | |
1370 assert(copy(dcm) == expd); | |
1371 dcm = "2.1"; | |
1372 expd = "2.1"; | |
1373 | |
1374 assert(copyAbs(dcm) == expd); | |
1375 dcm = "-1.00"; | |
1376 expd = "1.00"; | |
1377 assert(copyAbs(dcm) == expd); | |
1378 dcm = "101.5"; | |
1379 expd = "-101.5"; | |
1380 | |
1381 assert(copyNegate(dcm) == expd); | |
1382 Decimal dcm1; | |
1383 Decimal dcm2; | |
1384 dcm1 = "1.50"; | |
1385 dcm2 = "7.33"; | |
1386 expd = "1.50"; | |
1387 | |
1388 assert(copySign(dcm1, dcm2) == expd); | |
1389 dcm1 = "-1.50"; | |
1390 dcm2 = "7.33"; | |
1391 expd = "1.50"; | |
1392 assert(copySign(dcm1, dcm2) == expd); | |
1393 dcm1 = "1.50"; | |
1394 dcm2 = "-7.33"; | |
1395 expd = "-1.50"; | |
1396 assert(copySign(dcm1, dcm2) == expd); | |
1397 dcm1 = "-1.50"; | |
1398 dcm2 = "-7.33"; | |
1399 expd = "-1.50"; | |
1400 assert(copySign(dcm1, dcm2) == expd); | |
1401 writeln("passed"); | |
1402 } | |
1403 | |
1404 //-------------------------------- | |
1405 // plus, minus and abs functions | |
1406 //-------------------------------- | |
1407 | |
1408 /** | |
1409 * unary minus -- returns a copy with the opposite sign. | |
1410 * This operation may set flags -- equivalent to | |
1411 * subtract('0', b); | |
1412 */ | |
1413 Decimal minus(const Decimal dcm) { | |
1414 Decimal result; | |
1415 if(isInvalidOperation(dcm, result)) { | |
1416 return result; | |
1417 } | |
1418 result = copyNegate(dcm); | |
1419 round(result); | |
1420 return result; | |
1421 } | |
1422 | |
1423 /** | |
1424 * unary plus -- returns a copy. | |
1425 * This operation may set flags -- equivalent to | |
1426 * add('0', a); | |
1427 */ | |
1428 Decimal plus(const Decimal dcm) { | |
1429 Decimal result; | |
1430 if(isInvalidOperation(dcm, result)) { | |
1431 return result; | |
1432 } | |
1433 result = dcm; | |
1434 round(result); | |
1435 return result; | |
1436 } | |
1437 | |
1438 unittest { | |
1439 write("minus/plus..."); | |
1440 // NOTE: result should equal 0+this or 0-this | |
1441 Decimal zero = Decimal(0); | |
1442 Decimal dcm; | |
1443 Decimal expd; | |
1444 dcm = "1.3"; | |
1445 expd = zero + dcm; | |
1446 assert(+dcm == expd); | |
1447 dcm = "-1.3"; | |
1448 expd = zero + dcm; | |
1449 assert(+dcm == expd); | |
1450 dcm = "1.3"; | |
1451 expd = zero - dcm; | |
1452 assert(-dcm == expd); | |
1453 dcm = "-1.3"; | |
1454 expd = zero - dcm; | |
1455 assert(-dcm == expd); | |
1456 // TODO: add tests that check flags. | |
1457 writeln("passed"); | |
1458 } | |
1459 | |
1460 /// Returns a new Decimal equal to the absolute value of this Decimal. | |
1461 public Decimal abs(const Decimal dcm) { | |
1462 Decimal result; | |
1463 if(isInvalidOperation(dcm, result)) { | |
1464 return result; | |
1465 } | |
1466 result = copyAbs(dcm); | |
1467 round(result); | |
1468 return result; | |
1469 } | |
1470 | |
1471 unittest { | |
1472 // TODO: add rounding tests | |
1473 write("abs.........."); | |
1474 Decimal dcm; | |
1475 Decimal expd; | |
1476 dcm = "sNaN"; | |
1477 assert(abs(dcm).isQuiet); | |
1478 assert(context.flags && INVALID_OPERATION); | |
1479 dcm = "NaN"; | |
1480 assert(abs(dcm).isQuiet); | |
1481 assert(context.flags && INVALID_OPERATION); | |
1482 dcm = "Inf"; | |
1483 expd = "Inf"; | |
1484 assert(abs(dcm) == expd); | |
1485 dcm = "-Inf"; | |
1486 expd = "Inf"; | |
1487 assert(abs(dcm) == expd); | |
1488 dcm = "0"; | |
1489 expd = "0"; | |
1490 assert(abs(dcm) == expd); | |
1491 dcm = "-0"; | |
1492 expd = "0"; | |
1493 assert(abs(dcm) == expd); | |
1494 dcm = "2.1"; | |
1495 expd = "2.1"; | |
1496 assert(abs(dcm) == expd); | |
1497 dcm = -100; | |
1498 expd = 100; | |
1499 assert(abs(dcm) == expd); | |
1500 dcm = 101.5; | |
1501 expd = 101.5; | |
1502 assert(abs(dcm) == expd); | |
1503 dcm = -101.5; | |
1504 assert(abs(dcm) == expd); | |
1505 writeln("passed"); | |
1506 } | |
1507 | |
1508 public Decimal nextPlus(const Decimal dcm) { | |
1509 Decimal result; | |
1510 if (isInvalidOperation(dcm, result)) { | |
1511 return result; | |
1512 } | |
1513 if (dcm.isInfinite && dcm.sign) return -context.max(); | |
1514 int adjx = dcm.expo + dcm.digits - context.precision; | |
1515 if (adjx < context.eTiny) { | |
1516 return Decimal(true, 0, context.eTiny); | |
1517 } | |
1518 Decimal addend = Decimal(1, adjx); | |
1519 result = dcm + addend; | |
1520 if (result > context.max) { | |
1521 result = POS_INF; | |
1522 } | |
1523 return result; | |
1524 } | |
1525 | |
1526 unittest { | |
1527 write("next-plus...."); | |
1528 pushContext(); | |
1529 context.eMax = 999; | |
1530 context.eMin = -999; | |
1531 Decimal dcm; | |
1532 Decimal expd; | |
1533 dcm = 1; | |
1534 expd = "1.00000001"; | |
1535 assert(nextPlus(dcm) == expd); | |
1536 dcm = 10; | |
1537 expd = "10.0000001"; | |
1538 assert(nextPlus(dcm) == expd); | |
1539 dcm = 1E5; | |
1540 expd = "100000.001"; | |
1541 assert(nextPlus(dcm) == expd); | |
1542 dcm = 1E8; | |
1543 expd = "100000001"; | |
1544 assert(nextPlus(dcm) == expd); | |
1545 // num digits exceeds precision... | |
1546 dcm = "1234567891"; | |
1547 expd = "1.23456790E9"; | |
1548 assert(nextPlus(dcm) == expd); | |
1549 // result < tiny | |
1550 dcm = "-1E-1007"; | |
1551 expd = "-0E-1007"; | |
1552 assert(nextPlus(dcm) == expd); | |
1553 dcm = "-1.00000003"; | |
1554 expd = "-1.00000002"; | |
1555 assert(nextPlus(dcm) == expd); | |
1556 dcm = "-Infinity"; | |
1557 expd = "-9.99999999E+999"; | |
1558 assert(nextPlus(dcm) == expd); | |
1559 popContext(); | |
1560 writeln("passed"); | |
1561 } | |
1562 | |
1563 public Decimal nextMinus(const Decimal dcm) { | |
1564 Decimal result; | |
1565 if (isInvalidOperation(dcm, result)) { | |
1566 return result; | |
1567 } | |
1568 if (dcm.isInfinite && !dcm.sign) return context.max(); | |
1569 // This is necessary to catch the special case where ceff == 1 | |
1570 Decimal red = reduce(dcm); | |
1571 int adjx = red.expo + red.digits - context.precision; | |
1572 if (dcm.ceff == 1) adjx--; | |
1573 if (adjx < context.eTiny) { | |
1574 return Decimal(false, 0, context.eTiny); | |
1575 } | |
1576 Decimal addend = Decimal(1, adjx); | |
1577 result = dcm - addend; | |
1578 if (result < -context.max) { | |
1579 result = NEG_INF; | |
1580 } | |
1581 return result; | |
1582 } | |
1583 | |
1584 unittest { | |
1585 write("next-minus..."); | |
1586 pushContext(); | |
1587 context.eMax = 999; | |
1588 context.eMin = -999; | |
1589 Decimal dcm; | |
1590 Decimal expd; | |
1591 dcm = 1; | |
1592 expd = "0.999999999"; | |
1593 assert(nextMinus(dcm) == expd); | |
1594 dcm = "1E-1007"; | |
1595 expd = "0E-1007"; | |
1596 assert(nextMinus(dcm) == expd); | |
1597 dcm = "-1.00000003"; | |
1598 expd = "-1.00000004"; | |
1599 assert(nextMinus(dcm) == expd); | |
1600 dcm = "Infinity"; | |
1601 expd = "9.99999999E+999"; | |
1602 assert(nextMinus(dcm) == expd); | |
1603 popContext(); | |
1604 writeln("passed"); | |
1605 } | |
1606 | |
1607 public Decimal nextToward(const Decimal dcm1, const Decimal dcm2) { | |
1608 Decimal result; | |
1609 if (isInvalidOperation(dcm1, dcm2, result)) { | |
1610 return result; | |
1611 } | |
1612 int comp = icompare(dcm1, dcm2); | |
1613 if (comp < 0) return nextPlus(dcm1); | |
1614 if (comp > 0) return nextMinus(dcm1); | |
1615 result = copySign(dcm1, dcm2); | |
1616 round(result); | |
1617 return result; | |
1618 } | |
1619 | |
1620 unittest { | |
1621 write("next-toward.."); | |
1622 DecimalContext save = context; | |
1623 Decimal dcm1, dcm2; | |
1624 Decimal expd; | |
1625 dcm1 = 1; | |
1626 dcm2 = 2; | |
1627 expd = "1.00000001"; | |
1628 assert(nextToward(dcm1,dcm2) == expd); | |
1629 dcm1 = "-1E-1007"; | |
1630 dcm2 = 1; | |
1631 expd = "-0E-1007"; | |
1632 assert(nextToward(dcm1,dcm2) == expd); | |
1633 dcm1 = "-1.00000003"; | |
1634 dcm2 = 0; | |
1635 expd = "-1.00000002"; | |
1636 assert(nextToward(dcm1,dcm2) == expd); | |
1637 dcm1 = 1; | |
1638 dcm2 = 0; | |
1639 expd = "0.999999999"; | |
1640 assert(nextToward(dcm1,dcm2) == expd); | |
1641 dcm1 = "1E-1007"; | |
1642 dcm2 = -100; | |
1643 expd = "0E-1007"; | |
1644 assert(nextToward(dcm1,dcm2) == expd); | |
1645 dcm1 = "-1.00000003"; | |
1646 dcm2 = -10; | |
1647 expd = "-1.00000004"; | |
1648 assert(nextToward(dcm1,dcm2) == expd); | |
1649 dcm1 = "0.00"; | |
1650 dcm2 = "-0.0000"; | |
1651 expd = "-0.00"; | |
1652 assert(nextToward(dcm1,dcm2) == expd); | |
1653 context = save; | |
1654 writeln("passed"); | |
1655 } | |
1656 | |
1657 //-------------------------------- | |
1658 // comparison functions | |
1659 //-------------------------------- | |
1660 | |
1661 /// returns true if the numbers have the same exponent. | |
1662 public bool sameQuantum(const Decimal x, const Decimal y) { | |
1663 if (x.isNaN || y.isNaN) { | |
1664 return x.isNaN && y.isNaN; | |
1665 } | |
1666 if (x.isInfinite || y.isInfinite) { | |
1667 return x.isInfinite && y.isInfinite; | |
1668 } | |
1669 return x.expo == y.expo; | |
1670 } | |
1671 | |
1672 unittest { | |
1673 write("same-quantum."); | |
1674 Decimal op1; | |
1675 Decimal op2; | |
1676 op1 = "2.17"; | |
1677 op2 = "0.001"; | |
1678 assert(!sameQuantum(op1, op2)); | |
1679 op2 = "0.01"; | |
1680 assert(sameQuantum(op1, op2)); | |
1681 op2 = "0.1"; | |
1682 assert(!sameQuantum(op1, op2)); | |
1683 op2 = "1"; | |
1684 assert(!sameQuantum(op1, op2)); | |
1685 op1 = "Inf"; | |
1686 op2 = "Inf"; | |
1687 assert(sameQuantum(op1, op2)); | |
1688 op1 = "NaN"; | |
1689 op2 = "NaN"; | |
1690 assert(sameQuantum(op1, op2)); | |
1691 writeln("passed"); | |
1692 } | |
1693 | |
1694 public Decimal compare(const Decimal dcm1, const Decimal dcm2, | |
1695 const bool signal = false) { | |
1696 // sNaN is an invalid operand | |
1697 if (dcm1.isSignaling && dcm2.isSignaling) { | |
1698 return invalidOp(); | |
1699 } | |
1700 // qNaN is invalid if signal flag is set. | |
1701 if (signal && (dcm1.isNaN || dcm2.isNaN)) { | |
1702 return invalidOp(); | |
1703 } | |
1704 // NaN returns > any number, including NaN | |
1705 if (dcm1.isNaN) return ONE; | |
1706 if (dcm2.isNaN) return -ONE; | |
1707 | |
1708 if (dcm1.sign != dcm2.sign) { | |
1709 Decimal op1 = dcm1.sign ? -ONE : ONE; | |
1710 Decimal op2 = -op1; | |
1711 op1 = dcm1.isZero ? ZERO : op1; | |
1712 op2 = dcm2.isZero ? ZERO : op2; | |
1713 return op1 != op2 ? op1 : ZERO; | |
1714 } | |
1715 int diff = (dcm1.expo + dcm1.digits) - (dcm2.expo + dcm2.digits); | |
1716 if (!dcm1.sign) { | |
1717 if (diff > 0) return ONE; | |
1718 if (diff < 0) return -ONE; | |
1719 } | |
1720 else { | |
1721 if (diff > 0) return -ONE; | |
1722 if (diff < 0) return ONE; | |
1723 } | |
1724 Decimal result = dcm1 - dcm2; | |
1725 if (result.isZero) return ZERO; | |
1726 return result.sign ? -ONE : ONE; | |
1727 } | |
1728 | |
1729 public int icompare(const Decimal dcm1, const Decimal dcm2) { | |
1730 // sNaN is invalid operand | |
1731 // NaN returns > any number, including NaN | |
1732 if (dcm1.isSignaling) { | |
1733 invalidOp(); | |
1734 return 1; | |
1735 } | |
1736 if (dcm2.isSignaling) { | |
1737 invalidOp(); | |
1738 return -1; | |
1739 } | |
1740 // NaN returns > any number, including NaN | |
1741 if (dcm1.isNaN) return 1; | |
1742 if (dcm2.isNaN) return -1; | |
1743 | |
1744 if (dcm1.sign != dcm2.sign) { | |
1745 int op1 = dcm1.sign ? -1 : 1; | |
1746 int op2 = -op1; | |
1747 op1 = dcm1.isZero ? 0 : op1; | |
1748 op2 = dcm2.isZero ? 0 : op2; | |
1749 return op1 != op2 ? op1 : 0; | |
1750 } | |
1751 int diff = (dcm1.expo + dcm1.digits) - (dcm2.expo + dcm2.digits); | |
1752 if (!dcm1.sign) { | |
1753 if (diff > 0) return 1; | |
1754 if (diff < 0) return -1; | |
1755 } | |
1756 else { | |
1757 if (diff > 0) return -1; | |
1758 if (diff < 0) return 1; | |
1759 } | |
1760 Decimal result = dcm1 - dcm2; | |
1761 if (result.isZero) return 0; | |
1762 return result.sign ? -1 : 1; | |
1763 } | |
1764 | |
1765 unittest { | |
1766 write("compare......"); | |
1767 Decimal op1; | |
1768 Decimal op2; | |
1769 int result; | |
1770 op1 = "2.1"; | |
1771 op2 = "3"; | |
1772 result = icompare(op1, op2); | |
1773 assert(result == -1); | |
1774 op1 = "2.1"; | |
1775 op2 = "2.1"; | |
1776 result = icompare(op1, op2); | |
1777 assert(result == 0); | |
1778 op1 = "2.1"; | |
1779 op2 = "2.10"; | |
1780 result = icompare(op1, op2); | |
1781 assert(result == 0); | |
1782 op1 = "3"; | |
1783 op2 = "2.1"; | |
1784 result = icompare(op1, op2); | |
1785 assert(result == 1); | |
1786 op1 = "2.1"; | |
1787 op2 = "-3"; | |
1788 result = icompare(op1, op2); | |
1789 assert(result == 1); | |
1790 op1 = "-3"; | |
1791 op2 = "2.1"; | |
1792 result = icompare(op1, op2); | |
1793 assert(result == -1); | |
1794 op1 = -3; | |
1795 op2 = -4; | |
1796 result = icompare(op1, op2); | |
1797 assert(result == 1); | |
1798 op1 = -300; | |
1799 op2 = -4; | |
1800 result = icompare(op1, op2); | |
1801 assert(result == -1); | |
1802 op1 = 3; | |
1803 op2 = context.max; | |
1804 result = icompare(op1, op2); | |
1805 assert(result == -1); | |
1806 op1 = -3; | |
1807 op2 = -context.max; | |
1808 result = icompare(op1, op2); | |
1809 assert(result == 1); | |
1810 | |
1811 writeln("passed"); | |
1812 } | |
1813 | |
1814 /// Returns 0 if the numbers are equal and have the same representation | |
1815 public int compareTotal(const Decimal x, const Decimal y) { | |
1816 if (x.sign != y.sign) { | |
1817 return x.sign ? -1 : 1; | |
1818 } | |
1819 if (x.isQuiet || y.isQuiet) { | |
1820 if (x.isQuiet && y.isQuiet) { | |
1821 return 0; | |
1822 } | |
1823 return x.isQuiet ? 1 : -1; | |
1824 } | |
1825 if (x.isSignaling || y.isSignaling) { | |
1826 return 0; | |
1827 } | |
1828 if (x.isInfinite || y.isInfinite) { | |
1829 return 0; | |
1830 } | |
1831 int diff = (x.expo + x.digits) - (y.expo + y.digits); | |
1832 if (diff > 0) return 1; | |
1833 if (diff < 0) return -1; | |
1834 Decimal result = x - y; | |
1835 if (result.isZero) { | |
1836 if (x.expo > y.expo) return 1; | |
1837 if (x.expo < y.expo) return -1; | |
1838 return 0; | |
1839 } | |
1840 return result.sign ? -1 : 1; | |
1841 } | |
1842 | |
1843 unittest { | |
1844 write("comp-total..."); | |
1845 Decimal op1; | |
1846 Decimal op2; | |
1847 int result; | |
1848 op1 = "12.73"; | |
1849 op2 = "127.9"; | |
1850 result = compareTotal(op1, op2); | |
1851 assert(result == -1); | |
1852 op1 = "-127"; | |
1853 op2 = "12"; | |
1854 result = compareTotal(op1, op2); | |
1855 assert(result == -1); | |
1856 op1 = "12.30"; | |
1857 op2 = "12.3"; | |
1858 result = compareTotal(op1, op2); | |
1859 assert(result == -1); | |
1860 op1 = "12.30"; | |
1861 op2 = "12.30"; | |
1862 result = compareTotal(op1, op2); | |
1863 assert(result == 0); | |
1864 op1 = "12.3"; | |
1865 op2 = "12.300"; | |
1866 result = compareTotal(op1, op2); | |
1867 assert(result == 1); | |
1868 op1 = "12.3"; | |
1869 op2 = "NaN"; | |
1870 result = compareTotal(op1, op2); | |
1871 assert(result == -1); | |
1872 writeln("passed"); | |
1873 } | |
1874 | |
1875 int compareTotalMagnitude(const Decimal x, const Decimal y) { | |
1876 return compareTotal(copyAbs(x), copyAbs(y)); | |
1877 } | |
1878 | |
1879 // TODO: this is where the need for flags comes in. | |
1880 /** | |
1881 * Returns the maximum of the two operands (or NaN). | |
1882 * If either is an sNaN, or both are quiet NaNs, a NaN is returned. | |
1883 * Otherwise, Any (finite or infinite) number is larger than a NaN. | |
1884 * If they are not numerically equal, the larger is returned. | |
1885 * If they are numerically equal: | |
1886 * 1) If the signs differ, the one with the positive sign is returned. | |
1887 * 2) If they are positive, the one with the larger exponent is returned. | |
1888 * 3) If they are negative, the one with the smaller exponent is returned. | |
1889 * 4) Otherwise, they are indistinguishable; the first is returned. | |
1890 */ | |
1891 Decimal max(const Decimal op1, const Decimal op2) { | |
1892 // if both are NaNs or either is an sNan, return NaN. | |
1893 if (op1.isNaN && op2.isNaN || op1.isSignaling || op2.isSignaling) { | |
1894 return NaN; | |
1895 } | |
1896 // if one op is a quiet NaN return the other | |
1897 if (op1.isQuiet || op2.isQuiet) { | |
1898 return (op1.isQuiet) ? op2 : op1; | |
1899 } | |
1900 // if the signs differ, return the unsigned operand | |
1901 if (op1.sign != op2.sign) { | |
1902 return op1.sign ? op2 : op1; | |
1903 } | |
1904 // if not numerically equal, return the larger | |
1905 int comp = icompare(op1, op2); | |
1906 if (comp != 0) { | |
1907 return comp > 0 ? op1 : op2; | |
1908 } | |
1909 // if they have the same exponent they are identical, return either | |
1910 if (op1.expo == op2.expo) { | |
1911 return op1; | |
1912 } | |
1913 // if they are non-negative, return the one with larger exponent. | |
1914 if (op1.sign == 0) { | |
1915 return op1.expo > op2.expo ? op1 : op2; | |
1916 } | |
1917 // else they are negative; return the one with smaller exponent. | |
1918 return op1.expo > op2.expo ? op2 : op1; | |
1919 } | |
1920 | |
1921 unittest { | |
1922 write("max.........."); | |
1923 Decimal op1; | |
1924 Decimal op2; | |
1925 op1 = 3; | |
1926 op2 = 2; | |
1927 assert(max(op1, op2) == op1); | |
1928 op1 = -10; | |
1929 op2 = 3; | |
1930 assert(max(op1, op2) == op2); | |
1931 op1 = "1.0"; | |
1932 op2 = "1"; | |
1933 assert(max(op1, op2) == op2); | |
1934 op1 = "7"; | |
1935 op2 = "NaN"; | |
1936 assert(max(op1, op2) == op1); | |
1937 writeln("passed"); | |
1938 } | |
1939 | |
1940 /** | |
1941 * Returns the minimum of the two operands (or NaN). | |
1942 * If either is an sNaN, or both are quiet NaNs, a NaN is returned. | |
1943 * Otherwise, Any (finite or infinite) number is smaller than a NaN. | |
1944 * If they are not numerically equal, the smaller is returned. | |
1945 * If they are numerically equal: | |
1946 * 1) If the signs differ, the one with the negative sign is returned. | |
1947 * 2) If they are negative, the one with the larger exponent is returned. | |
1948 * 3) If they are positive, the one with the smaller exponent is returned. | |
1949 * 4) Otherwise, they are indistinguishable; the first is returned. | |
1950 */ | |
1951 Decimal min(const Decimal op1, const Decimal op2) { | |
1952 // if both are NaNs or either is an sNan, return NaN. | |
1953 if (op1.isNaN && op2.isNaN || op1.isSignaling || op2.isSignaling) { | |
1954 /+ Decimal result; | |
1955 result.flags = INVALID_OPERATION;+/ | |
1956 return NaN; | |
1957 } | |
1958 // if one op is a quiet NaN return the other | |
1959 if (op1.isQuiet || op2.isQuiet) { | |
1960 return (op1.isQuiet) ? op2 : op1; | |
1961 } | |
1962 // if the signs differ, return the unsigned operand | |
1963 if (op1.sign != op2.sign) { | |
1964 return op1.sign ? op1 : op2; | |
1965 } | |
1966 // if not numerically equal, return the smaller | |
1967 int comp = icompare(op1, op2); | |
1968 if (comp != 0) { | |
1969 return comp < 0 ? op1 : op2; | |
1970 } | |
1971 // if they have the same exponent they are identical, return either | |
1972 if (op1.expo == op2.expo) { | |
1973 return op1; | |
1974 } | |
1975 // if they are non-negative, return the one with smaller exponent. | |
1976 if (op1.sign == 0) { | |
1977 return op1.expo < op2.expo ? op1 : op2; | |
1978 } | |
1979 // else they are negative; return the one with larger exponent. | |
1980 return op1.expo < op2.expo ? op2 : op1; | |
1981 } | |
1982 | |
1983 unittest { | |
1984 write("min.........."); | |
1985 Decimal op1; | |
1986 Decimal op2; | |
1987 op1 = 3; | |
1988 op2 = 2; | |
1989 assert(min(op1, op2) == op2); | |
1990 op1 = -10; | |
1991 op2 = 3; | |
1992 assert(min(op1, op2) == op1); | |
1993 op1 = "1.0"; | |
1994 op2 = "1"; | |
1995 assert(min(op1, op2) == op1); | |
1996 op1 = "7"; | |
1997 op2 = "NaN"; | |
1998 assert(min(op1, op2) == op1); | |
1999 writeln("passed"); | |
2000 } | |
2001 | |
2002 //------------------------------------------ | |
2003 // | |
2004 // Binary Arithmetic Operations | |
2005 // | |
2006 //------------------------------------------ | |
2007 | |
2008 /** | |
2009 * Adds two Decimal numbers. | |
2010 * | |
2011 * This function corresponds to the "add and subtract" function | |
2012 * in the General Decimal Arithmetic Specification and is the basis | |
2013 * for the opAdd and opSub functions for the Decimal struct. | |
2014 */ | |
2015 Decimal add(const Decimal augend, const Decimal addend) { | |
2016 | |
2017 Decimal sum; | |
2018 // check for NaN operand(s) | |
2019 if (isInvalidOperation(augend, addend, sum)) { | |
2020 return sum; | |
2021 } | |
2022 // if both operands are infinite | |
2023 if (augend.isInfinite && addend.isInfinite) { | |
2024 // (+inf) + (-inf) => invalid operation | |
2025 if (augend.sign != addend.sign) { | |
2026 return invalidOp(); | |
2027 } | |
2028 // both infinite with same sign | |
2029 return augend; | |
2030 } | |
2031 | |
2032 /+ if (isInvalidAddition(augend, addend, sum)) { | |
2033 return sum; | |
2034 }+/ | |
2035 // TODO: is it okay to return the operand? is a copy implied? | |
2036 // only augend is infinite, | |
2037 if (augend.isInfinite) { | |
2038 return augend; | |
2039 } | |
2040 // only addend is infinite | |
2041 if (addend.isInfinite) { | |
2042 return addend; | |
2043 } | |
2044 | |
2045 // align the operands | |
2046 alignOps(augend, addend); | |
2047 | |
2048 // add(0, 0) | |
2049 if (augend.isZero && addend.isZero) { | |
2050 sum = augend; | |
2051 sum.sign = augend.sign && addend.sign; | |
2052 return sum; | |
2053 } | |
2054 | |
2055 // at this point, the result will be finite and not zero | |
2056 // (before rounding) | |
2057 sum.clear(); | |
2058 | |
2059 // if operands have the same sign... | |
2060 if (augend.sign == addend.sign) { | |
2061 sum.ceff = augend.ceff + addend.ceff; | |
2062 sum.sign = augend.sign; | |
2063 } | |
2064 // ...else operands have different signs | |
2065 else { | |
2066 sum.ceff = augend.ceff - addend.ceff; | |
2067 sum.sign = augend.sign; | |
2068 if (sum.ceff < BIG_ZERO) { | |
2069 sum.ceff = -sum.ceff; | |
2070 sum.sign = !sum.sign; | |
2071 } | |
2072 } | |
2073 // set the number of digits and the exponent | |
2074 sum.digits = numDigits(sum.ceff, augend.digits); | |
2075 sum.expo = augend.expo; | |
2076 | |
2077 // round the result | |
2078 round(sum); | |
2079 return sum; | |
2080 } // end add(augend, addend) | |
2081 | |
2082 /** | |
2083 * Subtracts two Decimal numbers. | |
2084 * | |
2085 * This function corresponds to the "add and subtract" function | |
2086 * in the General Decimal Arithmetic Specification and is the basis | |
2087 * for the opAdd and opSub functions for the Decimal struct. | |
2088 */ | |
2089 Decimal subtract(const Decimal minuend, const Decimal subtrahend) { | |
2090 return add(minuend, copyNegate(subtrahend)); | |
2091 } // end subtract(minuend, subtrahend) | |
2092 | |
2093 // TODO: these tests need to be cleaned up to rely less on strings | |
2094 // and to check the NaN, Inf combinations better. | |
2095 unittest { | |
2096 write("add.........."); | |
2097 Decimal dcm1 = Decimal("12"); | |
2098 Decimal dcm2 = Decimal("7.00"); | |
2099 Decimal sum = add(dcm1, dcm2); | |
2100 assert(sum.toString() == "19.00"); | |
2101 dcm1 = Decimal("1E+2"); | |
2102 dcm2 = Decimal("1E+4"); | |
2103 sum = add(dcm1, dcm2); | |
2104 assert(sum.toString() == "1.01E+4"); | |
2105 dcm1 = Decimal("1.3"); | |
2106 dcm2 = Decimal("1.07"); | |
2107 sum = subtract(dcm1, dcm2); | |
2108 assert(sum.toString() == "0.23"); | |
2109 dcm2 = Decimal("1.30"); | |
2110 sum = subtract(dcm1, dcm2); | |
2111 assert(sum.toString() == "0.00"); | |
2112 dcm2 = Decimal("2.07"); | |
2113 sum = subtract(dcm1, dcm2); | |
2114 assert(sum.toString() == "-0.77"); | |
2115 dcm1 = "Inf"; | |
2116 dcm2 = 1; | |
2117 sum = add(dcm1, dcm2); | |
2118 assert(sum.toString() == "Infinity"); | |
2119 dcm1 = "NaN"; | |
2120 dcm2 = 1; | |
2121 sum = add(dcm1, dcm2); | |
2122 assert(sum.isQuiet); | |
2123 dcm2 = "Infinity"; | |
2124 sum = add(dcm1, dcm2); | |
2125 assert(sum.isQuiet); | |
2126 dcm1 = 1; | |
2127 sum = subtract(dcm1, dcm2); | |
2128 assert(sum.toString() == "-Infinity"); | |
2129 dcm1 = "-0"; | |
2130 dcm2 = 0; | |
2131 sum = subtract(dcm1, dcm2); | |
2132 assert(sum.toString() == "-0"); | |
2133 writeln("passed"); | |
2134 } | |
2135 | |
2136 Decimal multiply(const Decimal multiplier, const Decimal multiplicand) { | |
2137 | |
2138 Decimal product; | |
2139 if (isInvalidMultiplication(multiplier, multiplicand, product)) { | |
2140 return product; | |
2141 } | |
2142 if (multiplier.isInfinite || multiplicand.isInfinite) { | |
2143 product = Decimal.infinity; | |
2144 product.sign = multiplier.sign ^ multiplicand.sign; | |
2145 return product; | |
2146 } | |
2147 product.clear(); | |
2148 product.ceff = multiplier.ceff * multiplicand.ceff; | |
2149 product.expo = multiplier.expo + multiplicand.expo; | |
2150 product.sign = multiplier.sign ^ multiplicand.sign; | |
2151 product.digits = numDigits(product.ceff, multiplier.digits + multiplicand.digits); | |
2152 round(product); | |
2153 return product; | |
2154 } | |
2155 | |
2156 unittest { | |
2157 write("multiply....."); | |
2158 Decimal op1, op2, result; | |
2159 op1 = Decimal("1.20"); | |
2160 op2 = 3; | |
2161 result = op1 * op2; | |
2162 assert(result.toString() == "3.60"); | |
2163 op1 = 7; | |
2164 result = op1 * op2; | |
2165 assert(result.toString() == "21"); | |
2166 op1 = Decimal("0.9"); | |
2167 op2 = Decimal("0.8"); | |
2168 result = op1 * op2; | |
2169 assert(result.toString() == "0.72"); | |
2170 op1 = Decimal("0.9"); | |
2171 op2 = Decimal("-0.0"); | |
2172 result = op1 * op2; | |
2173 assert(result.toString() == "-0.00"); | |
2174 op1 = Decimal(654321); | |
2175 op2 = Decimal(654321); | |
2176 result = op1 * op2; | |
2177 assert(result.toString() == "4.28135971E+11"); | |
2178 op1 = -1; | |
2179 op2 = "Infinity"; | |
2180 result = op1 * op2; | |
2181 assert(result.toString() == "-Infinity"); | |
2182 op1 = -1; | |
2183 op2 = 0; | |
2184 result = op1 * op2; | |
2185 assert(result.toString() == "-0"); | |
2186 writeln("passed"); | |
2187 } | |
2188 | |
2189 Decimal fma(const Decimal multiplier, const Decimal multiplicand, | |
2190 const Decimal addend) { | |
2191 Decimal product; | |
2192 if (isInvalidMultiplication(multiplier, multiplicand, product)) { | |
2193 return product; | |
2194 } | |
2195 product.clear(); | |
2196 product.ceff = multiplier.ceff * multiplicand.ceff; | |
2197 product.expo = multiplier.expo + multiplicand.expo; | |
2198 product.sign = multiplier.sign ^ multiplicand.sign; | |
2199 product.digits = numDigits(product.ceff, multiplier.digits + multiplicand.digits); | |
2200 return add(product, addend); | |
2201 } | |
2202 | |
2203 unittest { | |
2204 write("fma.........."); | |
2205 Decimal op1; | |
2206 Decimal op2; | |
2207 Decimal op3; | |
2208 Decimal result; | |
2209 op1 = 3; | |
2210 op2 = 5; | |
2211 op3 = 7; | |
2212 result = (fma(op1, op2, op3)); | |
2213 assert(result == Decimal(22)); | |
2214 op1 = 3; | |
2215 op2 = -5; | |
2216 op3 = 7; | |
2217 result = (fma(op1, op2, op3)); | |
2218 assert(result == Decimal(-8)); | |
2219 op1 = "888565290"; | |
2220 op2 = "1557.96930"; | |
2221 op3 = "-86087.7578"; | |
2222 result = (fma(op1, op2, op3)); | |
2223 assert(result == Decimal("1.38435736E+12")); | |
2224 writeln("passed"); | |
2225 } | |
2226 | |
2227 bool isZeroDividend(const Decimal dividend, const Decimal divisor, | |
2228 Decimal quotient) { | |
2229 if (dividend.isZero()) { | |
2230 quotient.spval = SpVal.ZERO; | |
2231 quotient.ceff = BIG_ZERO; | |
2232 quotient.expo = 0; | |
2233 quotient.digits = dividend.digits; | |
2234 quotient.sign = dividend.sign; | |
2235 return true; | |
2236 } | |
2237 return false; | |
2238 } | |
2239 | |
2240 Decimal divide(const Decimal dividend, const Decimal divisor) { | |
2241 | |
2242 Decimal quotient; | |
2243 if (isInvalidDivision(dividend, divisor, quotient)) { | |
2244 return quotient; | |
2245 } | |
2246 if (isZeroDividend(dividend, divisor, quotient)) { | |
2247 return quotient; | |
2248 } | |
2249 quotient.clear(); | |
2250 | |
2251 Decimal temp = dividend; | |
2252 int adjust = 0; | |
2253 while (temp.ceff < divisor.ceff) { | |
2254 temp.ceff *= 10; | |
2255 adjust++; | |
2256 } | |
2257 bool complete = false; | |
2258 while (!complete) { | |
2259 // repeated subtraction | |
2260 while (divisor.ceff <= temp.ceff) { | |
2261 temp.ceff -= divisor.ceff; | |
2262 quotient.ceff++; | |
2263 } | |
2264 // check for done | |
2265 if (temp.ceff == 0 && adjust >= 0 | |
2266 || numDigits(quotient.ceff, 1) == context.precision + 2) { | |
2267 complete = true; | |
2268 } | |
2269 else { | |
2270 // bump the quotient and temp by 10 | |
2271 quotient.ceff *= 10; | |
2272 temp.ceff *= 10; | |
2273 adjust++; | |
2274 } | |
2275 quotient.digits = numDigits(quotient.ceff, context.precision); | |
2276 } | |
2277 quotient.expo = temp.expo - divisor.expo - adjust; | |
2278 quotient.sign = temp.sign ^ divisor.sign; | |
2279 round(quotient); | |
2280 return quotient; | |
2281 } | |
2282 | |
2283 Decimal edivide(const Decimal dividend, const Decimal divisor) { | |
2284 Decimal quotient; | |
2285 if (isInvalidDivision(dividend, divisor, quotient)) { | |
2286 return quotient; | |
2287 } | |
2288 if (isZeroDividend(dividend, divisor, quotient)) { | |
2289 return quotient; | |
2290 } | |
2291 quotient.clear(); | |
2292 Decimal denom = dividend; | |
2293 Decimal numer = divisor; | |
2294 // align operands | |
2295 int diff = denom.expo - numer.expo; | |
2296 if (diff < 0) { | |
2297 numer.ceff *= pow10(-diff); | |
2298 } | |
2299 if (diff > 0) { | |
2300 numer.ceff *= pow10(diff); | |
2301 } | |
2302 numer.digits = numDigits(numer.ceff, 1); | |
2303 denom.digits = numDigits(denom.ceff, 1); | |
2304 int shift = 2 + context.precision - denom.digits + numer.digits; | |
2305 if (shift > 0) { | |
2306 denom.ceff *= pow10(shift); | |
2307 denom.expo -= shift; | |
2308 } | |
2309 quotient.ceff = denom.ceff / numer.ceff; | |
2310 quotient.expo = denom.expo - numer.expo; | |
2311 quotient.sign = denom.sign ^ numer.sign; | |
2312 quotient.digits = numDigits(quotient.ceff, context.precision); | |
2313 round(quotient); | |
2314 return quotient; | |
2315 } | |
2316 | |
2317 unittest { | |
2318 write("divide......."); | |
2319 Decimal dcm1, dcm2; | |
2320 Decimal expd; | |
2321 dcm1 = 1; | |
2322 dcm2 = 3; | |
2323 Decimal quotient = edivide(dcm1, dcm2); | |
2324 expd = "0.333333333"; | |
2325 assert(quotient == expd); | |
2326 assert(quotient.toString() == expd.toString()); | |
2327 dcm1 = 2; | |
2328 dcm2 = 3; | |
2329 quotient = edivide(dcm1, dcm2); | |
2330 expd = "0.666666667"; | |
2331 assert(quotient == expd); | |
2332 dcm1 = 5; | |
2333 dcm2 = 2; | |
2334 quotient = divide(dcm1, dcm2); | |
2335 expd = "2.5"; | |
2336 assert(quotient == expd); | |
2337 assert(quotient.toString() == expd.toString()); | |
2338 dcm1 = 1; | |
2339 dcm2 = 10; | |
2340 expd = 0.1; | |
2341 quotient = divide(dcm1, dcm2); | |
2342 assert(quotient == expd); | |
2343 assert(quotient.toString() == expd.toString()); | |
2344 dcm1 = "8.00"; | |
2345 dcm2 = 2; | |
2346 expd = "4.00"; | |
2347 quotient = divide(dcm1, dcm2); | |
2348 assert(quotient == expd); | |
2349 assert(quotient.toString() == expd.toString()); | |
2350 dcm1 = "2.400"; | |
2351 dcm2 = "2.0"; | |
2352 expd = "1.20"; | |
2353 quotient = divide(dcm1, dcm2); | |
2354 assert(quotient == expd); | |
2355 assert(quotient.toString() == expd.toString()); | |
2356 dcm1 = 1000; | |
2357 dcm2 = 100; | |
2358 expd = 10; | |
2359 quotient = divide(dcm1, dcm2); | |
2360 assert(quotient == expd); | |
2361 assert(quotient.toString() == expd.toString()); | |
2362 dcm2 = 1; | |
2363 quotient = divide(dcm1, dcm2); | |
2364 expd = 1000; | |
2365 assert(quotient == expd); | |
2366 assert(quotient.toString() == expd.toString()); | |
2367 dcm1 = "2.40E+6"; | |
2368 dcm2 = 2; | |
2369 expd = "1.20E+6"; | |
2370 quotient = divide(dcm1, dcm2); | |
2371 assert(quotient == expd); | |
2372 assert(quotient.toString() == expd.toString()); | |
2373 writeln("passed"); | |
2374 } | |
2375 | |
2376 Decimal divideInteger(const Decimal dividend, const Decimal divisor) { | |
2377 Decimal quotient; | |
2378 if (isInvalidDivision(dividend, divisor, quotient)) { | |
2379 return quotient; | |
2380 } | |
2381 if (isZeroDividend(dividend, divisor, quotient)) { | |
2382 return quotient; | |
2383 } | |
2384 quotient.clear(); | |
2385 Decimal denom = dividend; | |
2386 Decimal numer = divisor; | |
2387 // align operands | |
2388 int diff = denom.expo - numer.expo; | |
2389 if (diff < 0) { | |
2390 numer.ceff *= pow10(-diff); | |
2391 } | |
2392 if (diff > 0) { | |
2393 denom.ceff *= pow10(diff); | |
2394 } | |
2395 quotient.ceff = denom.ceff / numer.ceff; | |
2396 quotient.expo = 0; | |
2397 quotient.sign = denom.sign ^ numer.sign; | |
2398 quotient.digits = numDigits(quotient.ceff, context.precision); | |
2399 if (quotient.ceff == 0) quotient.spval = SpVal.ZERO; | |
2400 return quotient; | |
2401 } | |
2402 | |
2403 unittest { | |
2404 write("div-int......"); | |
2405 Decimal dividend; | |
2406 Decimal divisor; | |
2407 Decimal quotient; | |
2408 Decimal expd; | |
2409 dividend = 2; | |
2410 divisor = 3; | |
2411 quotient = divideInteger(dividend, divisor); | |
2412 expd = 0; | |
2413 assert(quotient == expd); | |
2414 dividend = 10; | |
2415 quotient = divideInteger(dividend, divisor); | |
2416 expd = 3; | |
2417 assert(quotient == expd); | |
2418 dividend = 1; | |
2419 divisor = "0.3"; | |
2420 quotient = divideInteger(dividend, divisor); | |
2421 assert(quotient == expd); | |
2422 writeln("passed"); | |
2423 } | |
2424 | |
2425 Decimal remainder(const Decimal dividend, const Decimal divisor) { | |
2426 Decimal quotient; | |
2427 if (isInvalidDivision(dividend, divisor, quotient)) { | |
2428 return quotient; | |
2429 } | |
2430 if (isZeroDividend(dividend, divisor, quotient)) { | |
2431 return quotient; | |
2432 } | |
2433 quotient = dividend - divisor * divideInteger(dividend, divisor); | |
2434 return quotient; | |
2435 } | |
2436 | |
2437 unittest { | |
2438 write("remainder...."); | |
2439 Decimal dividend; | |
2440 Decimal divisor; | |
2441 Decimal quotient; | |
2442 Decimal expected; | |
2443 dividend = "2.1"; | |
2444 divisor = 3; | |
2445 quotient = remainder(dividend, divisor); | |
2446 expected = "2.1"; | |
2447 assert(quotient == expected); | |
2448 dividend = 10; | |
2449 quotient = remainder(dividend, divisor); | |
2450 expected = 1; | |
2451 assert(quotient == expected); | |
2452 dividend = -10; | |
2453 quotient = remainder(dividend, divisor); | |
2454 expected = -1; | |
2455 assert(quotient == expected); | |
2456 dividend = 10.2; | |
2457 divisor = 1; | |
2458 quotient = remainder(dividend, divisor); | |
2459 expected = "0.2"; | |
2460 assert(quotient == expected); | |
2461 dividend = 10; | |
2462 divisor = 0.3; | |
2463 quotient = remainder(dividend, divisor); | |
2464 expected = "0.1"; | |
2465 assert(quotient == expected); | |
2466 dividend = 3.6; | |
2467 divisor = 1.3; | |
2468 quotient = remainder(dividend, divisor); | |
2469 expected = "1.0"; | |
2470 assert(quotient == expected); | |
2471 writeln("passed"); | |
2472 } | |
2473 | |
2474 //-------------------------------- | |
2475 // rounding | |
2476 //-------------------------------- | |
2477 | |
2478 public Decimal rint(const Decimal dec){ | |
2479 if (dec.isSignaling) return invalidOp(); | |
2480 if (dec.isSpecial) return dec; | |
2481 if (dec.expo >= 0) return dec; | |
2482 pushContext(); | |
2483 context.precision = dec.digits; | |
2484 Decimal result = quantize(dec, ONE); | |
2485 popContext(); | |
2486 return result; | |
2487 } | |
2488 | |
2489 public Decimal nearbyint(const Decimal dec){ | |
2490 // this operation shouldn't affect the inexact or rounded flags | |
2491 // so we'll save them in case they were already set. | |
2492 bool inexact = context.getFlag(INEXACT); | |
2493 bool rounded = context.getFlag(ROUNDED); | |
2494 Decimal result = rint(dec); | |
2495 context.setFlag(INEXACT, inexact); | |
2496 context.setFlag(ROUNDED, rounded); | |
2497 return result; | |
2498 } | |
2499 | |
2500 unittest { | |
2501 write("rnd-int-ex..."); | |
2502 Decimal dec; | |
2503 Decimal expd; | |
2504 Decimal actual; | |
2505 dec = 2.1; | |
2506 expd = 2; | |
2507 actual = rint(dec); | |
2508 assert(actual == expd); | |
2509 dec = 100; | |
2510 expd = 100; | |
2511 assert(rint(dec) == expd); | |
2512 assert(rint(dec).toString() == expd.toString()); | |
2513 dec = "100.0"; | |
2514 assert(rint(dec) == expd); | |
2515 assert(rint(dec).toString() == expd.toString()); | |
2516 dec = "101.5"; | |
2517 expd = 102; | |
2518 assert(rint(dec) == expd); | |
2519 assert(rint(dec).toString() == expd.toString()); | |
2520 dec = "-101.5"; | |
2521 expd = -102; | |
2522 assert(rint(dec) == expd); | |
2523 assert(rint(dec).toString() == expd.toString()); | |
2524 dec = "10E+5"; | |
2525 expd = "1.0E+6"; | |
2526 assert(rint(dec) == expd); | |
2527 assert(rint(dec).toString() == expd.toString()); | |
2528 dec = "7.89E+77"; | |
2529 expd = "7.89E+77"; | |
2530 assert(rint(dec) == expd); | |
2531 assert(rint(dec).toString() == expd.toString()); | |
2532 dec = "-Inf"; | |
2533 expd = "-Infinity"; | |
2534 assert(rint(dec) == expd); | |
2535 assert(rint(dec).toString() == expd.toString()); | |
2536 writeln("passed"); | |
2537 } | |
2538 | |
2539 /** | |
2540 * Clips the coefficient of the number to the specified precision. | |
2541 * Returns the remainder for adjustments based on rounding mode. | |
2542 * Sets the ROUNDED and INEXACT flags. | |
2543 */ | |
2544 private BigInt shorten(ref Decimal number) { | |
2545 BigInt remainder = BIG_ZERO; | |
2546 int diff = number.digits - context.precision; | |
2547 if (diff <= 0) { | |
2548 return remainder; | |
2549 } | |
2550 context.setFlag(ROUNDED); | |
2551 if (context.precision == 0) { | |
2552 remainder = number.ceff; | |
2553 number.ceff = 0; | |
2554 number.digits = 1; | |
2555 } | |
2556 else { | |
2557 BigInt divisor = pow10(diff); | |
2558 remainder = number.ceff % divisor; | |
2559 number.ceff /= divisor; | |
2560 number.digits = context.precision; | |
2561 number.expo += diff; | |
2562 } | |
2563 if (remainder != BIG_ZERO) { | |
2564 context.setFlag(INEXACT); | |
2565 } | |
2566 return remainder; | |
2567 } | |
2568 | |
2569 /** | |
2570 * Increments the coefficient by 1. If this causes an overflow, divides by 10. | |
2571 */ | |
2572 private void increment(ref BigInt number) { | |
2573 number++; | |
2574 if (lastDigit(number) == 0) { | |
2575 number /= 10; | |
2576 } | |
2577 } | |
2578 | |
2579 // TODO: need to signal inexact and rounded. | |
2580 private void roundByMode(ref Decimal number) { | |
2581 BigInt remainder = shorten(number); | |
2582 | |
2583 // if the rounded flag is not set by the shorten operation, return | |
2584 if (!context.getFlag(ROUNDED)) { | |
2585 return; | |
2586 } | |
2587 // if the remainder is zero, return | |
2588 if (remainder == BIG_ZERO) { | |
2589 return; | |
2590 } | |
2591 | |
2592 switch (context.mode) { | |
2593 case Rounding.DOWN: | |
2594 return; | |
2595 case Rounding.HALF_UP: | |
2596 if (firstDigit(remainder) >= 5) { | |
2597 increment(number.ceff); | |
2598 } | |
2599 return; | |
2600 case Rounding.HALF_EVEN: | |
2601 BigInt test = 5 * pow10(numDigits(remainder,1)-1); | |
2602 int result = remainder.opCmp(test); | |
2603 if (result > 0) { | |
2604 increment(number.ceff); | |
2605 return; | |
2606 } | |
2607 if (result < 0) { | |
2608 return; | |
2609 } | |
2610 // if last digit is odd... | |
2611 if (lastDigit(number.ceff) & 1 == 1) { | |
2612 increment(number.ceff); | |
2613 } | |
2614 return; | |
2615 case Rounding.CEILING: | |
2616 if (!number.sign && remainder != BIG_ZERO) { | |
2617 increment(number.ceff); | |
2618 } | |
2619 return; | |
2620 case Rounding.FLOOR: | |
2621 if (number.sign && remainder != BIG_ZERO) { | |
2622 increment(number.ceff); | |
2623 } | |
2624 return; | |
2625 case Rounding.HALF_DOWN: | |
2626 if (firstDigit(remainder) > 5) { | |
2627 increment(number.ceff); | |
2628 } | |
2629 return; | |
2630 case Rounding.UP: | |
2631 if (remainder != BIG_ZERO) { | |
2632 increment(number.ceff); | |
2633 } | |
2634 return; | |
2635 } // end switch(mode) | |
2636 } // end roundByMode() | |
2637 | |
2638 public void round(ref Decimal number) { | |
2639 if (!number.isFinite) return; | |
2640 | |
2641 context.clearFlags(); | |
2642 // check for subnormal | |
2643 bool subnormal = false; | |
2644 if (number.isSubnormal()) { | |
2645 context.setFlag(SUBNORMAL); | |
2646 subnormal = true; | |
2647 } | |
2648 | |
2649 // check for overflow | |
2650 if (number.overflow()) { | |
2651 context.setFlag(OVERFLOW); | |
2652 switch (context.mode) { | |
2653 case Rounding.HALF_UP: | |
2654 case Rounding.HALF_EVEN: | |
2655 case Rounding.HALF_DOWN: | |
2656 case Rounding.UP: | |
2657 bool sign = number.sign; | |
2658 number = POS_INF; | |
2659 number.sign = sign; | |
2660 break; | |
2661 case Rounding.DOWN: | |
2662 bool sign = number.sign; | |
2663 number = context.max; | |
2664 number.sign = sign; | |
2665 break; | |
2666 case Rounding.CEILING: | |
2667 if (number.sign) { | |
2668 number = context.max; | |
2669 number.sign = true; | |
2670 } | |
2671 else { | |
2672 number = POS_INF; | |
2673 } | |
2674 break; | |
2675 case Rounding.FLOOR: | |
2676 if (number.sign) { | |
2677 number = NEG_INF; | |
2678 } else { | |
2679 number = context.max; | |
2680 } | |
2681 break; | |
2682 } | |
2683 context.setFlag(INEXACT); | |
2684 context.setFlag(ROUNDED); | |
2685 return; | |
2686 } | |
2687 roundByMode(number); | |
2688 // check for underflow | |
2689 if (number.isSubnormal /+&& number.isInexact+/) { | |
2690 context.setFlag(SUBNORMAL); | |
2691 int diff = context.eTiny - number.adjustedExponent(); | |
2692 if (diff > number.digits) { | |
2693 number.ceff = 0; | |
2694 number.expo = context.eTiny; | |
2695 } else if (diff > 0) { | |
2696 writeln("We got a tiny one!"); | |
2697 } | |
2698 } | |
2699 // check for zero | |
2700 if (number.spval == SpVal.CLEAR && number.ceff == BIG_ZERO) { | |
2701 number.spval = SpVal.ZERO; | |
2702 // subnormal rounding to zero == clamped | |
2703 // Spec. p. 51 | |
2704 if (subnormal) { | |
2705 context.setFlag(CLAMPED); | |
2706 } | |
2707 return; | |
2708 } | |
2709 } // end round() | |
2710 | |
2711 unittest { | |
2712 write("round........"); | |
2713 Decimal before = Decimal(1234567890); | |
2714 Decimal after = before; | |
2715 pushContext(); | |
2716 context.precision = 3; | |
2717 round(after); | |
2718 assert(after.toString() == "1.23E+9"); | |
2719 after = before; | |
2720 context.precision = 4; | |
2721 round(after); | |
2722 assert(after.toString() == "1.235E+9"); | |
2723 after = before; | |
2724 context.precision = 5; | |
2725 round(after); | |
2726 assert(after.toString() == "1.2346E+9"); | |
2727 after = before; | |
2728 context.precision = 6; | |
2729 round(after); | |
2730 assert(after.toString() == "1.23457E+9"); | |
2731 after = before; | |
2732 context.precision = 7; | |
2733 round(after); | |
2734 assert(after.toString() == "1.234568E+9"); | |
2735 after = before; | |
2736 context.precision = 8; | |
2737 round(after); | |
2738 assert(after.toString() == "1.2345679E+9"); | |
2739 before = "1235"; | |
2740 after = before; | |
2741 context.precision = 3; | |
2742 round(after); | |
2743 assert(after.toAbstract() == "[0,124,1]"); | |
2744 before = "12359"; | |
2745 after = before; | |
2746 context.precision = 3; | |
2747 round(after); | |
2748 assert(after.toAbstract() == "[0,124,2]"); | |
2749 before = "1245"; | |
2750 after = before; | |
2751 context.precision = 3; | |
2752 round(after); | |
2753 assert(after.toAbstract() == "[0,124,1]"); | |
2754 before = "12459"; | |
2755 after = before; | |
2756 context.precision = 3; | |
2757 round(after); | |
2758 assert(after.toAbstract() == "[0,125,2]"); | |
2759 popContext(); | |
2760 writeln("passed"); | |
2761 } | |
2762 | |
2763 public void setDigits(ref Decimal number) { | |
2764 int diff = number.digits - context.precision; | |
2765 if (diff > 0) { | |
2766 round(number); | |
2767 } | |
2768 else if (diff < 0) { | |
2769 number.ceff *= pow10(-diff); | |
2770 number.expo += diff; | |
2771 } | |
2772 number.digits = context.precision; | |
2773 } | |
2774 | |
2775 /** | |
2776 * Returns the number which is equal in value and sign | |
2777 * to the first operand and which has its exponent set | |
2778 * to be equal to the exponent of the second operand. | |
2779 */ | |
2780 // TODO: this has unusual flag rules | |
2781 Decimal quantize(const Decimal dcm1, const Decimal dcm2) { | |
2782 Decimal result; | |
2783 if (isInvalidOperation(dcm1, dcm2, result)) { | |
2784 return result; | |
2785 } | |
2786 if (dcm1.isInfinite != dcm2.isInfinite() || | |
2787 dcm2.isInfinite != dcm1.isInfinite()) { | |
2788 return invalidOp(); | |
2789 } | |
2790 if (dcm1.isInfinite() && dcm2.isInfinite()) { | |
2791 return dcm1.dup; | |
2792 } | |
2793 result = dcm1; | |
2794 int diff = dcm1.expo - dcm2.expo; | |
2795 if (diff == 0) { | |
2796 return result; | |
2797 } | |
2798 if (diff > 0) { | |
2799 result.ceff *= pow10(diff); | |
2800 result.digits += diff; | |
2801 result.expo = dcm2.expo; | |
2802 if (result.digits > context.precision) { | |
2803 result = NaN; | |
2804 } | |
2805 return result; | |
2806 } | |
2807 else { | |
2808 pushContext(); | |
2809 context.precision = | |
2810 -diff > dcm1.digits ? 0 : dcm1.digits + diff; | |
2811 round(result); | |
2812 result.expo = dcm2.expo; | |
2813 popContext(); | |
2814 return result; | |
2815 } | |
2816 } | |
2817 | |
2818 unittest { | |
2819 write("quantize....."); | |
2820 Decimal op1; | |
2821 Decimal op2; | |
2822 Decimal result; | |
2823 Decimal expd; | |
2824 string str; | |
2825 op1 = "2.17"; | |
2826 op2 = "0.001"; | |
2827 expd = "2.170"; | |
2828 result = quantize(op1, op2); | |
2829 assert(result == expd); | |
2830 op1 = "2.17"; | |
2831 op2 = "0.01"; | |
2832 expd = "2.17"; | |
2833 result = quantize(op1, op2); | |
2834 assert(result == expd); | |
2835 op1 = "2.17"; | |
2836 op2 = "0.1"; | |
2837 expd = "2.2"; | |
2838 result = quantize(op1, op2); | |
2839 assert(result == expd); | |
2840 op1 = "2.17"; | |
2841 op2 = "1e+0"; | |
2842 expd = "2"; | |
2843 result = quantize(op1, op2); | |
2844 assert(result == expd); | |
2845 op1 = "2.17"; | |
2846 op2 = "1e+1"; | |
2847 expd = "0E+1"; | |
2848 result = quantize(op1, op2); | |
2849 assert(result.toString() == expd.toString()); | |
2850 op1 = "-Inf"; | |
2851 op2 = "Infinity"; | |
2852 expd = "-Infinity"; | |
2853 result = quantize(op1, op2); | |
2854 assert(result == expd); | |
2855 op1 = "2"; | |
2856 op2 = "Infinity"; | |
2857 expd = "NaN"; | |
2858 result = quantize(op1, op2); | |
2859 assert(result.toString() == expd.toString()); | |
2860 op1 = "-0.1"; | |
2861 op2 = "1"; | |
2862 expd = "-0"; | |
2863 result = quantize(op1, op2); | |
2864 assert(result.toString() == expd.toString()); | |
2865 op1 = "-0"; | |
2866 op2 = "1e+5"; | |
2867 expd = "-0E+5"; | |
2868 result = quantize(op1, op2); | |
2869 assert(result.toString() == expd.toString()); | |
2870 op1 = "+35236450.6"; | |
2871 op2 = "1e-2"; | |
2872 expd = "NaN"; | |
2873 result = quantize(op1, op2); | |
2874 assert(result.toString() == expd.toString()); | |
2875 op1 = "-35236450.6"; | |
2876 op2 = "1e-2"; | |
2877 expd = "NaN"; | |
2878 result = quantize(op1, op2); | |
2879 assert(result.toString() == expd.toString()); | |
2880 op1 = "217"; | |
2881 op2 = "1e-1"; | |
2882 expd = "217.0"; | |
2883 result = quantize(op1, op2); | |
2884 assert(result.toString() == expd.toString()); | |
2885 op1 = "217"; | |
2886 op2 = "1e+0"; | |
2887 expd = "217"; | |
2888 result = quantize(op1, op2); | |
2889 assert(result.toString() == expd.toString()); | |
2890 op1 = "217"; | |
2891 op2 = "1e+1"; | |
2892 expd = "2.2E+2"; | |
2893 result = quantize(op1, op2); | |
2894 assert(result.toString() == expd.toString()); | |
2895 op1 = "217"; | |
2896 op2 = "1e+2"; | |
2897 expd = "2E+2"; | |
2898 result = quantize(op1, op2); | |
2899 assert(result.toString() == expd.toString()); | |
2900 assert(result == expd); | |
2901 writeln("passed"); | |
2902 } | |
2903 | |
2904 /** | |
2905 * Reduces operand to simplest form. All trailing zeros are removed. | |
2906 */ | |
2907 // TODO: has non-standard flag setting | |
2908 Decimal reduce(const Decimal dcm) { | |
2909 Decimal result; | |
2910 if (isInvalidOperation(dcm, result)) { | |
2911 return result; | |
2912 } | |
2913 result = dcm; | |
2914 if (!result.isFinite()) { | |
2915 return result; | |
2916 } | |
2917 BigInt temp = result.ceff % 10; | |
2918 while (result.ceff != 0 && temp == 0) { | |
2919 result.expo++; | |
2920 result.ceff = result.ceff / 10; | |
2921 temp = result.ceff % 10; | |
2922 } | |
2923 if (result.ceff == 0) { | |
2924 result.spval = SpVal.ZERO; | |
2925 result.expo = 0; | |
2926 } | |
2927 result.digits = numDigits(result.ceff); | |
2928 return result; | |
2929 } | |
2930 | |
2931 unittest { | |
2932 write("reduce......."); | |
2933 Decimal dec; | |
2934 Decimal red; | |
2935 string str; | |
2936 dec = "2.1"; | |
2937 str = "2.1"; | |
2938 red = reduce(dec); | |
2939 assert(red.toString() == str); | |
2940 dec = "-2.0"; | |
2941 str = "-2"; | |
2942 red = reduce(dec); | |
2943 assert(red.toString() == str); | |
2944 dec = "1.200"; | |
2945 str = "1.2"; | |
2946 red = reduce(dec); | |
2947 assert(red.toString() == str); | |
2948 dec = "-120"; | |
2949 str = "-1.2E+2"; | |
2950 red = reduce(dec); | |
2951 assert(red.toString() == str); | |
2952 dec = "120.00"; | |
2953 str = "1.2E+2"; | |
2954 red = reduce(dec); | |
2955 assert(red.toString() == str); | |
2956 writeln("passed"); | |
2957 } | |
2958 | |
2959 private: | |
2960 | |
2961 /** | |
2962 * Sets the invalid-operation flag and | |
2963 * returns a quiet NaN. | |
2964 */ | |
2965 Decimal invalidOp() { | |
2966 context.flags |= INVALID_OPERATION; | |
2967 return NaN; | |
2968 } | |
2969 | |
2970 /** | |
2971 * aligns the two operands by raising the smaller exponent | |
2972 * to the value of the larger exponent, and adjusting the | |
2973 * coefficient so the value remains the same. | |
2974 */ | |
2975 void alignOps(ref Decimal op1, ref Decimal op2) { | |
2976 int diff = op1.expo - op2.expo; | |
2977 if (diff > 0) { | |
2978 op1.ceff *= pow10(diff); | |
2979 op1.expo = op2.expo; | |
2980 } | |
2981 else if (diff < 0) { | |
2982 op2.ceff *= pow10(-diff); | |
2983 op2.expo = op1.expo; | |
2984 } | |
2985 } | |
2986 | |
2987 /* | |
2988 * "The result of any arithmetic operation which has an operand | |
2989 * which is a NaN (a quiet NaN or a signaling NaN) is [s,qNaN] | |
2990 * or [s,qNaN,d]. The sign and any diagnostic information is copied | |
2991 * from the first operand which is a signaling NaN, or if neither is | |
2992 * signaling then from the first operand which is a NaN." | |
2993 * -- General Decimal Arithmetic Specification, p. 24 | |
2994 */ | |
2995 bool isInvalidOperation(const Decimal dcm1, const Decimal dcm2, | |
2996 ref Decimal result) { | |
2997 // if either operand is an sNaN... | |
2998 if (dcm1.isSignaling || dcm2.isSignaling) { | |
2999 // set the result to the first sNaN operand | |
3000 result = dcm1.isSignaling ? dcm1 : dcm2; | |
3001 // retain sign and payload; convert to qNaN | |
3002 result.spval = SpVal.QNAN; | |
3003 // flag the invalid operation | |
3004 context.flags |= INVALID_OPERATION; | |
3005 return true; | |
3006 } | |
3007 // ...else if either operand is a qNaN... | |
3008 if (dcm1.isQuiet || dcm2.isQuiet) { | |
3009 // set the result to the first qNaN operand | |
3010 result = dcm1.isQuiet ? dcm1 : dcm2; | |
3011 // flag the invalid operation | |
3012 context.flags |= INVALID_OPERATION; | |
3013 return true; | |
3014 } | |
3015 // ...otherwise, no flags are set and result is unchanged | |
3016 return false; | |
3017 } | |
3018 | |
3019 unittest { | |
3020 write("invalid......"); | |
3021 Decimal dcm; | |
3022 Decimal expd; | |
3023 Decimal actual; | |
3024 | |
3025 dcm = "sNaN123"; | |
3026 expd = "NaN123"; | |
3027 actual = abs(dcm); | |
3028 assert(actual.isQuiet); | |
3029 assert(context.flags && INVALID_OPERATION); | |
3030 assert(actual.toAbstract == expd.toAbstract); | |
3031 dcm = "NaN123"; | |
3032 actual = abs(dcm); | |
3033 assert(actual.isQuiet); | |
3034 assert(context.flags && INVALID_OPERATION); | |
3035 assert(actual.toAbstract == expd.toAbstract); | |
3036 | |
3037 dcm = "sNaN123"; | |
3038 expd = "NaN123"; | |
3039 actual = -dcm; | |
3040 assert(actual.isQuiet); | |
3041 assert(context.flags && INVALID_OPERATION); | |
3042 assert(actual.toAbstract == expd.toAbstract); | |
3043 dcm = "NaN123"; | |
3044 actual = -dcm; | |
3045 assert(actual.isQuiet); | |
3046 assert(context.flags && INVALID_OPERATION); | |
3047 assert(actual.toAbstract == expd.toAbstract); | |
3048 writeln("passed"); | |
3049 } | |
3050 | |
3051 /* | |
3052 * "The result of any arithmetic operation which has an operand | |
3053 * which is a NaN (a quiet NaN or a signaling NaN) is [s,qNaN] | |
3054 * or [s,qNaN,d]. The sign and any diagnostic information is copied | |
3055 * from the first operand which is a signaling NaN, or if neither is | |
3056 * signaling then from the first operand which is a NaN." | |
3057 * -- General Decimal Arithmetic Specification, p. 24 | |
3058 */ | |
3059 bool isInvalidOperation(const Decimal dcm, ref Decimal result) { | |
3060 // if the operand is an sNaN... | |
3061 if (dcm.isSignaling) { | |
3062 // set the result to the sNaN operand | |
3063 result = dcm; | |
3064 // retain sign and payload; convert to qNaN | |
3065 result.spval = SpVal.QNAN; | |
3066 // flag the invalid operation | |
3067 context.flags |= INVALID_OPERATION; | |
3068 return true; | |
3069 } | |
3070 // ...else if the operand is a qNaN... | |
3071 if (dcm.isQuiet) { | |
3072 // set the result to the qNaN operand | |
3073 result = dcm; | |
3074 // flag the invalid operation | |
3075 context.flags |= INVALID_OPERATION; | |
3076 return true; | |
3077 } | |
3078 // ...otherwise, no flags are set and result is unchanged | |
3079 return false; | |
3080 } | |
3081 | |
3082 // TODO: add unit tests here for operations that apply | |
3083 unittest { | |
3084 | |
3085 } | |
3086 | |
3087 /+/* | |
3088 * -- General Decimal Arithmetic Specification, p. 52, "Invalid operation" | |
3089 */ | |
3090 bool isInvalidAddition(Decimal op1, Decimal op2, ref Decimal result) { | |
3091 if (isInvalidOperation(op1, op2, result)) { | |
3092 return true; | |
3093 } | |
3094 // if both operands are infinite | |
3095 if (op1.isInfinite && op2.isInfinite) { | |
3096 // (+inf) + (-inf) => invalid operation | |
3097 if (op1.sign != op2.sign) { | |
3098 result = invalidOp(); | |
3099 return true; | |
3100 } | |
3101 } | |
3102 return false; | |
3103 }+/ | |
3104 | |
3105 /* | |
3106 * -- General Decimal Arithmetic Specification, p. 52, "Invalid operation" | |
3107 */ | |
3108 bool isInvalidMultiplication(Decimal op1, Decimal op2, ref Decimal result) { | |
3109 if (isInvalidOperation(op1, op2, result)) { | |
3110 return true; | |
3111 } | |
3112 if (op1.isZero && op2.isInfinite || op1.isInfinite && op2.isZero) { | |
3113 result = NaN; | |
3114 return true; | |
3115 } | |
3116 return false; | |
3117 } | |
3118 | |
3119 /* | |
3120 * -- General Decimal Arithmetic Specification, p. 52, "Invalid operation" | |
3121 */ | |
3122 bool isInvalidDivision(Decimal dividend, Decimal divisor, ref Decimal quotient) { | |
3123 if (isInvalidOperation(dividend, divisor, quotient)) { | |
3124 return true; | |
3125 } | |
3126 if (divisor.isZero()) { | |
3127 if (dividend.isZero()) { | |
3128 quotient = invalidOp(); | |
3129 } | |
3130 else { | |
3131 quotient.spval = SpVal.INF; | |
3132 context.flags |= DIVISION_BY_ZERO; | |
3133 quotient.ceff = BIG_ZERO; | |
3134 quotient.sign = dividend.sign ^ divisor.sign; | |
3135 } | |
3136 return true; | |
3137 } | |
3138 return false; | |
3139 } | |
3140 | |
3141 //-------------------------------- | |
3142 // unit tests | |
3143 //-------------------------------- | |
3144 | |
3145 //-------------------------------- | |
3146 // additions to BigInt | |
3147 //-------------------------------- | |
3148 | |
3149 private: | |
3150 static immutable BigInt BIG_ZERO = { [0] }; | |
3151 static immutable BigInt BIG_ONE = { [1] }; | |
3152 static immutable BigInt BIG_FIVE = { [5] }; | |
3153 static immutable BigInt BIG_TEN = { [10] }; | |
3154 | |
3155 /** | |
3156 * Returns the number of decimal digits in BigInt value. | |
3157 * There are probably significant efficiency gains available. | |
3158 **/ | |
3159 uint numDigits(const BigInt big, int n = 1) { | |
3160 if (big == 0) return 1; | |
3161 if (n <= 0) n = 1; | |
3162 int m = n; | |
3163 while (big < pow10(m-1)) m--; | |
3164 if (m != n) return m; | |
3165 while (big >= pow10(m)) m++; | |
3166 return m; | |
3167 } | |
3168 | |
3169 /** | |
3170 * Returns a BigInt with the value of 10 raised to the specified power. | |
3171 * There are probably significant efficiency gains available. | |
3172 **/ | |
3173 public BigInt pow10(uint power) { | |
3174 static immutable int BILLION = 1000000000; | |
3175 static immutable int array[10] = | |
3176 [ 1, 10, 100, 1000, 10000, 100000, 1000000, | |
3177 10000000, 100000000, BILLION ]; | |
3178 | |
3179 if (power < 10) { | |
3180 return BigInt(array[power]); | |
3181 } | |
3182 BigInt big = BigInt(BILLION); | |
3183 power -= 9; | |
3184 int quo = power / 9; | |
3185 int rem = power % 9; | |
3186 for (int i = 0; i < quo; i++) { | |
3187 big *= BILLION; | |
3188 } | |
3189 return big * array[rem]; | |
3190 } | |
3191 | |
3192 /** | |
3193 * Returns the first decimal digit of the specified BigInt. | |
3194 */ | |
3195 uint firstDigit(BigInt big) { | |
3196 uint digits = numDigits(big, 1); | |
3197 if (digits == 0) { | |
3198 return 0; | |
3199 } | |
3200 BigInt bfd = big / pow10(digits - 1); | |
3201 uint ifd; | |
3202 bfd.castTo(ifd); | |
3203 return ifd; | |
3204 } | |
3205 | |
3206 /** | |
3207 * Returns the last decimal digit of the specified BigInt. | |
3208 */ | |
3209 uint lastDigit(BigInt big) { | |
3210 return big % 10; | |
3211 } | |
3212 | |
3213 unittest { | |
3214 write("bigint......."); | |
3215 string str = "1"; | |
3216 BigInt big = pow10(0); | |
3217 assert(str == big.toString); | |
3218 for (int i = 1; i < 35; i++) { | |
3219 str ~= "0"; | |
3220 big = pow10(i); | |
3221 assert(str == big.toString); | |
3222 assert(numDigits(big) == str.length); | |
3223 } | |
3224 writeln("passed"); | |
3225 } | |
3226 | |
3227 public void main() { | |
3228 writeln("Hello, world!"); | |
3229 /+ writeln("eTiny = ", context.eTiny); | |
3230 writeln("tiny min = ", Decimal(1, context.eTiny)); | |
3231 writeln("tiny min = ", Decimal(1, context.eTiny - 1)); | |
3232 writeln("max = ", context.max()); | |
3233 writeln("max1 = ", Decimal(999999999, 99)); | |
3234 writeln("dig = ", context.dig()); | |
3235 writeln("eps = ", context.epsilon()); | |
3236 writeln("smallest = ", context.min_normal()*context.epsilon()); | |
3237 writeln("1/epsilon = ", Decimal(1)/context.epsilon()); | |
3238 writeln("max * min = ", context.max * context.min_normal); | |
3239 writeln("mant_dig = ", context.mant_dig); | |
3240 writeln("min_exp = ", context.min_exp); | |
3241 writeln("max_exp = ", context.max_exp);+/ | |
3242 | |
3243 | |
3244 // TODO: this next one goes crazy -- shows need for checks on construction | |
3245 // TODO: turns out this is being converted to a double (or real) and | |
3246 // then it's really weird. | |
3247 // writeln("bigger = ", Decimal(999999999999)); | |
3248 // float f = float.max; | |
3249 // TODO: convert these to assserts | |
3250 /+ writeln("f.max = ", f); | |
3251 writeln("f.min = ", float.min); | |
3252 writeln("f = ", 2 * float.min); | |
3253 writeln("d.max = ", Decimal.max()); | |
3254 writeln("d.min = ", Decimal.min_normal()); | |
3255 writeln("2 * d.min = ", 2 * Decimal.min_normal()); | |
3256 writeln("0.1 * d.min = ", 0.1 * Decimal.min_normal());+/ | |
3257 // TODO: move this to unittesting | |
3258 /+ Decimal dec = Decimal(PI); | |
3259 writeln("pi = ", dec ); | |
3260 dec = Decimal(PI, 19); | |
3261 writeln("pi = ", dec ); | |
3262 dec = Decimal(1.3, 25); | |
3263 writeln("pi = ", dec ); | |
3264 dec = Decimal(0.1, 25); | |
3265 writeln("pi = ", dec ); | |
3266 dec = Decimal(2.0, 25); | |
3267 writeln("pi = ", dec ); | |
3268 dec = Decimal(200.5, 25); | |
3269 writeln("pi = ", dec ); | |
3270 writeln(double.dig); | |
3271 writeln(real.dig);+/ | |
3272 } | |
3273 |