API  1.0.0
CPDecimal.j
Go to the documentation of this file.
1 /*
2  * CPDecimal.j
3  * Foundation
4  *
5  * Created by Stephen Paul Ierodiaconou
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22  /*
23  Ported From GNUStep :
24 
25  NSDecimal functions
26  Copyright (C) 2000 Free Software Foundation, Inc.
27 
28  Written by: Fred Kiefer <[email protected]>
29  Created: July 2000
30 
31  This file is part of the GNUstep Base Library.
32 
33  This library is free software; you can redistribute it and/or
34  modify it under the terms of the GNU Lesser General Public
35  License as published by the Free Software Foundation; either
36  version 2 of the License, or (at your option) any later version.
37 
38  This library is distributed in the hope that it will be useful,
39  but WITHOUT ANY WARRANTY; without even the implied warranty of
40  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41  Library General Public License for more details.
42 
43  You should have received a copy of the GNU Lesser General Public
44  License along with this library; if not, write to the Free
45  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
46  Boston, MA 02111 USA.
47 
48  <title>NSDecimal class reference</title>
49  $Date: 2008-06-12 04:44:00 -0600 (Thu, 12 Jun 2008) $ $Revision: 26630 $
50  */
51 
52 
53 @typedef CPDecimal
54 
55 // Decimal size limits
59 
60 // Scale for no Rounding
62 
63 // CPCalculationError enum
64 CPCalculationNoError = 0;
69 
70 //CPRoundingMode Enum
71 @typedef CPRoundingMode
76 _CPRoundHalfDown = 5; // Private API rounding mode used by CPNumberFormatter.
77 
78 //Exceptions
79 CPDecimalNumberOverflowException = @"CPDecimalNumberOverflowException";
80 CPDecimalNumberUnderflowException = @"CPDecimalNumberUnderflowException";
81 CPDecimalNumberExactnessException = @"CPDecimalNumberExactnessException";
82 CPDecimalNumberDivideByZeroException = @"CPDecimalNumberDivideByZeroException";
83 
84 /*
85 Initialisers for NSDecimal do not exist so here I have created my own.
86 The coefficient is called the 'mantissa' in this implementation as this is what Cocoa calls it.
87 
88 CPDecimal format:
89  ._mantissa : CPArray, containing each digit of the number as an unsigned integer.
90  ._exponent : integer, the exponent of the number as an signed integer.
91  ._isNegative : BOOL, sign of number
92  ._isCompact : BOOL, has number been compacted.
93  ._isNaN : BOOL, is NaN (Not a number) i.e. number is invalid.
94 */
95 
103 // FIXME: locale support and Cocoaify, needs to accept .1 and leading 0s
104 function CPDecimalMakeWithString(string, locale)
105 {
106  if (!string)
107  return CPDecimalMakeNaN();
108 
109  // Regexp solution as found in JSON spec, with working regexp (I added groupings)
110  // Test here: http://www.regexplanet.com/simple/index.html
111  // Info from: http://stackoverflow.com/questions/638565/parsing-scientific-notation-sensibly
112  // ([+\-]?)((?:0|[0-9]\d*)) - integer part, can have leading zeros (follows Cocoa behaviour)
113  // (?:\.(\d*))? - optional decimal part plus number in group
114  // (?:[eE]([+\-]?)(\d+))? - optional exponent part plus number in group
115  // group 0: string, 1: sign, 2: integer, 3: decimal, 4: exponent sign, 5: exponent
116 
117  // Note: this doesn't accept .01 for example, should it?
118  // If yes simply add '?' after integer part group, i.e. ([+\-]?)((?:0|[1-9]\d*)?)
119  // Note: now it accept .01 style.
120  var matches = string.match(new RegExp("^([+\\-]?)((?:0|[0-9]\\d*)?)(?:\\.(\\d*))?(?:[eE]([+\\-]?)(\\d+))?$"));
121 
122  if (!matches)
123  return CPDecimalMakeNaN();
124 
125  var ds = matches[1],
126  intpart = matches[2],
127  decpart = matches[3],
128  es = matches[4],
129  exp = matches[5];
130 
131  var isNegative = NO;
132 
133  if (ds && ds === "-")
134  isNegative = YES;
135 
136  var exponent = 0;
137 
138  if (exp)
139  exponent = parseInt(exp) * ((es && es === "-")?-1:1);
140 
141  if (decpart) // push decimal point to last digit, then let compact handle the zeros
142  exponent -= decpart.length;
143 
144  var inputlength = (intpart?intpart.length:0) + (decpart?decpart.length:0);
145 
146  if (inputlength > CPDecimalMaxDigits)
147  {
148  // input is too long, increase exponent and truncate
149  exponent += inputlength - CPDecimalMaxDigits;
150  }
151  else if (inputlength === 0)
152  {
153  return CPDecimalMakeNaN();
154  }
155 
156  if (exponent > CPDecimalMaxExponent || exponent < CPDecimalMinExponent)
157  return CPDecimalMakeNaN();
158 
159  // Representation internally starts at most significant digit
160  var m = [],
161  i = 0;
162 
163  for (; i < (intpart?intpart.length:0); i++)
164  {
165  if (i >= CPDecimalMaxDigits)
166  break; // truncate
167  Array.prototype.push.call(m, parseInt(intpart.charAt(i)));
168  }
169 
170  var j = 0;
171 
172  for (; j < (decpart?decpart.length:0); j++)
173  {
174  if ((i + j) >= CPDecimalMaxDigits)
175  break; // truncate
176 
177  Array.prototype.push.call(m, parseInt(decpart.charAt(j)));
178  }
179 
180  var dcm = {_exponent:exponent, _isNegative:isNegative, _isCompact:NO, _isNaN:NO, _mantissa:m};
181  CPDecimalCompact(dcm);
182 
183  return dcm;
184 }
185 
193 function CPDecimalMakeWithParts(mantissa, exponent)
194 {
195  var m = [],
196  isNegative = NO;
197 
198  if (mantissa < 0)
199  {
200  isNegative = YES;
201  mantissa = ABS(mantissa);
202  }
203 
204  if (mantissa == 0)
205  Array.prototype.push.call(m, 0);
206 
207  if (exponent > CPDecimalMaxExponent || exponent < CPDecimalMinExponent)
208  return CPDecimalMakeNaN();
209 
210  // remaining digits are disposed of via truncation
211  while ((mantissa > 0) && (m.length < CPDecimalMaxDigits))
212  {
213  Array.prototype.unshift.call(m, parseInt(mantissa % 10));
214  mantissa = FLOOR(mantissa / 10);
215  }
216 
217  var dcm = {_exponent:exponent, _isNegative:isNegative, _isCompact:YES, _isNaN:NO, _mantissa:m};
218 
219  CPDecimalCompact(dcm);
220 
221  return dcm;
222 }
223 
230 {
231  return CPDecimalMakeWithParts(0, 0);
232 }
233 
240 {
241  return CPDecimalMakeWithParts(1, 0);
242 }
243 
244 
251 {
252  var d = CPDecimalMakeWithParts(0, 0);
253  d._isNaN = YES;
254  return d;
255 }
256 
257 // private methods
258 function _CPDecimalMakeMaximum()
259 {
260  var s = @"",
261  i = 0;
262  for (; i < CPDecimalMaxDigits; i++)
263  s += "9";
264  s += "e" + CPDecimalMaxExponent;
265  return CPDecimalMakeWithString(s);
266 }
267 
268 function _CPDecimalMakeMinimum()
269 {
270  var s = @"-",
271  i = 0;
272  for (; i < CPDecimalMaxDigits; i++)
273  s += "9";
274  s += "e" + CPDecimalMaxExponent;
275  return CPDecimalMakeWithString(s);
276 }
277 
283 function CPDecimalIsZero(dcm)
284 {
285  // exponent doesn't matter as long as mantissa = 0
286  if (!dcm._isNaN)
287  {
288  for (var i = 0; i < dcm._mantissa.length; i++)
289  if (dcm._mantissa[i] !== 0)
290  return NO;
291 
292  return YES;
293  }
294 
295  return NO;
296 }
297 
303 function CPDecimalIsOne(dcm)
304 {
305  CPDecimalCompact(dcm);
306 
307  // exponent doesn't matter as long as mantissa = 0
308  if (!dcm._isNaN)
309  {
310  if (dcm._mantissa && (dcm._mantissa.length == 1) && (dcm._mantissa[0] == 1))
311  return YES;
312  }
313  return NO;
314 }
315 
316 //private method to copy attribute values
317 function _CPDecimalSet(t, s)
318 {
319  // should all be [x copy] ?
320  t._exponent = s._exponent;
321  t._isNegative = s._isNegative;
322  t._isCompact = s._isCompact;
323  t._isNaN = s._isNaN;
324  t._mantissa = Array.prototype.slice.call(s._mantissa, 0);
325 }
326 
327 function _CPDecimalSetZero(result)
328 {
329  result._mantissa = [0];
330  result._exponent = 0;
331  result._isNegative = NO;
332  result._isCompact = YES;
333  result._isNaN = NO;
334 }
335 
336 function _CPDecimalSetOne(result)
337 {
338  result._mantissa = [1];
339  result._exponent = 0;
340  result._isNegative = NO;
341  result._isCompact = YES;
342  result._isNaN = NO;
343 }
344 
351 {
352  return (dcm._isNaN)?YES:NO;
353 }
354 
361 function CPDecimalCopy(dcm)
362 {
363  return {_exponent:dcm._exponent,
364  _isNegative:dcm._isNegative,
365  _isCompact:dcm._isCompact,
366  _isNaN:dcm._isNaN,
367  _mantissa:Array.prototype.slice.call(dcm._mantissa, 0)
368  };
369 }
370 
379 function CPDecimalCompare(leftOperand, rightOperand)
380 {
381  if (leftOperand._isNaN && rightOperand._isNaN)
382  return CPOrderedSame;
383 
384  if (leftOperand._isNegative != rightOperand._isNegative)
385  {
386  if (rightOperand._isNegative)
387  return CPOrderedDescending;
388  else
389  return CPOrderedAscending;
390  }
391 
392  // Before comparing number size check if zero (dont use CPDecimalIsZero as it is more computationally intensive)
393  var leftIsZero = (leftOperand._mantissa.length == 1 && leftOperand._mantissa[0] == 0),
394  rightIsZero = (rightOperand._mantissa.length == 1 && rightOperand._mantissa[0] == 0),
395  // Sign is the same, quick check size (length + exp)
396  s1 = leftOperand._exponent + leftOperand._mantissa.length,
397  s2 = rightOperand._exponent + rightOperand._mantissa.length;
398 
399  if (leftIsZero && rightIsZero)
400  return CPOrderedSame;
401 
402  if (leftIsZero || (s1 < s2 && !rightIsZero))
403  {
404  if (rightOperand._isNegative)
405  return CPOrderedDescending;
406  else
407  return CPOrderedAscending;
408  }
409  if (rightIsZero || (s1 > s2 && !leftIsZero))
410  {
411  if (leftOperand._isNegative)
412  return CPOrderedAscending;
413  else
414  return CPOrderedDescending;
415  }
416 
417  // Same size, so check mantissa
418  var l = MIN(leftOperand._mantissa.length, rightOperand._mantissa.length),
419  i = 0;
420 
421  for (; i < l; i++)
422  {
423  var d = rightOperand._mantissa[i] - leftOperand._mantissa[i];
424 
425  if (d > 0)
426  {
427  if (rightOperand._isNegative)
428  return CPOrderedDescending;
429  else
430  return CPOrderedAscending;
431  }
432  if (d < 0)
433  {
434  if (rightOperand._isNegative)
435  return CPOrderedAscending;
436  else
437  return CPOrderedDescending;
438  }
439  }
440 
441  // Same digits, check length
442  if (leftOperand._mantissa.length > rightOperand._mantissa.length)
443  {
444  if (rightOperand._isNegative)
445  return CPOrderedAscending;
446  else
447  return CPOrderedDescending;
448  }
449  if (leftOperand._mantissa.length < rightOperand._mantissa.length)
450  {
451  if (rightOperand._isNegative)
452  return CPOrderedDescending;
453  else
454  return CPOrderedAscending;
455  }
456 
457  return CPOrderedSame;
458 }
459 
460 // GNUSteps addition. This is standard O(n) complexity.
461 // longMode makes the addition not round for up to double max digits, this is
462 // to preserve precision during multiplication
463 function _SimpleAdd(result, leftOperand, rightOperand, roundingMode, longMode)
464 {
465  var factor = (longMode)?2:1;
466 
467  _CPDecimalSet(result, leftOperand);
468 
469  var j = leftOperand._mantissa.length - rightOperand._mantissa.length,
470  l = rightOperand._mantissa.length,
471  i = l - 1,
472  carry = 0,
473  error = CPCalculationNoError;
474 
475  // Add all the digits
476  for (; i >= 0; i--)
477  {
478  var d = rightOperand._mantissa[i] + result._mantissa[i + j] + carry;
479  if (d >= 10)
480  {
481  d = d % 10; // a division. subtraction and conditions faster?
482  carry = 1;
483  }
484  else
485  carry = 0;
486 
487  result._mantissa[i + j] = d;
488  }
489 
490  if (carry)
491  {
492  for (i = j - 1; i >= 0; i--)
493  {
494  if (result._mantissa[i] != 9)
495  {
496  result._mantissa[i]++;
497  carry = 0;
498  break;
499  }
500  result._mantissa[i] = 0;
501  }
502 
503  if (carry)
504  {
505  Array.prototype.splice.call(result._mantissa, 0, 0, 1);
506 
507  // The number must be shifted to the right
508  if ((CPDecimalMaxDigits * factor) == leftOperand._mantissa.length)
509  {
510  var scale = - result._exponent - 1;
511  CPDecimalRound(result, result, scale, roundingMode);
512  }
513 
514  if (CPDecimalMaxExponent < result._exponent)
515  {
516  result._isNaN = YES;
517  error = CPCalculationOverflow;
518  result._exponent = CPDecimalMaxExponent;
519  }
520  }
521  }
522  return error;
523 }
524 
534 function CPDecimalAdd(result, leftOperand, rightOperand, roundingMode, longMode)
535 {
536  if (leftOperand._isNaN || rightOperand._isNaN)
537  {
538  result._isNaN = YES;
539  return CPCalculationNoError;
540  }
541 
542  // check for zero
543  if (CPDecimalIsZero(leftOperand))
544  {
545  _CPDecimalSet(result, rightOperand);
546  return CPCalculationNoError;
547  }
548 
549  if (CPDecimalIsZero(rightOperand))
550  {
551  _CPDecimalSet(result, leftOperand);
552  return CPCalculationNoError;
553  }
554 
555  var n1 = CPDecimalCopy(leftOperand),
556  n2 = CPDecimalCopy(rightOperand);
557 
558  // For different signs use subtraction
559  if (leftOperand._isNegative != rightOperand._isNegative)
560  {
561  if (leftOperand._isNegative)
562  {
563  n1._isNegative = NO;
564  return CPDecimalSubtract(result, rightOperand, n1, roundingMode);
565  }
566  else
567  {
568  n2._isNegative = NO;
569  return CPDecimalSubtract(result, leftOperand, n2, roundingMode);
570  }
571  }
572 
573  var normerror = CPDecimalNormalize(n1, n2, roundingMode, longMode);
574 
575  // below is equiv. of simple compare
576  var comp = 0,
577  ll = n1._mantissa.length,
578  lr = n2._mantissa.length;
579 
580  if (ll == lr)
581  comp = CPOrderedSame;
582  else if (ll > lr)
583  comp = CPOrderedDescending;
584  else
585  comp = CPOrderedAscending;
586 
587  // both negative, make positive
588  if (leftOperand._isNegative)
589  {
590  n1._isNegative = NO;
591  n2._isNegative = NO;
592 
593  // SimpleCompare does not look at sign
594  if (comp == CPOrderedDescending)
595  {
596  adderror = _SimpleAdd(result, n1, n2, roundingMode, longMode);
597  }
598  else
599  {
600  adderror = _SimpleAdd(result, n2, n1, roundingMode, longMode);
601  }
602 
603  result._isNegative = YES;
604 
605  // swap sign over over/underflow exception
606  if (CPCalculationUnderflow == adderror)
607  adderror = CPCalculationOverflow;
608  else if (CPCalculationUnderflow == adderror)
609  adderror = CPCalculationUnderflow;
610  }
611  else
612  {
613  if (comp == CPOrderedAscending)
614  {
615  adderror = _SimpleAdd(result, n2, n1, roundingMode, longMode);
616  }
617  else
618  {
619  adderror = _SimpleAdd(result, n1, n2, roundingMode, longMode);
620  }
621  }
622 
623  CPDecimalCompact(result);
624 
625  if (adderror == CPCalculationNoError)
626  return normerror;
627  else
628  return adderror;
629 }
630 
631 // GNUStep port internal subtract
632 function _SimpleSubtract(result, leftOperand, rightOperand, roundingMode)
633 {
634  var error = CPCalculationNoError,
635  borrow = 0,
636  l = rightOperand._mantissa.length,
637  j = leftOperand._mantissa.length - l,
638  i = l - 1;
639 
640  _CPDecimalSet(result, leftOperand);
641 
642  // Now subtract all digits
643  for (; i >= 0; i--)
644  {
645  var d = result._mantissa[i + j] - rightOperand._mantissa[i] - borrow;
646 
647  if (d < 0)
648  {
649  d = d + 10;
650  borrow = 1;
651  }
652  else
653  borrow = 0;
654 
655  result._mantissa[i + j] = d;
656  }
657 
658  if (borrow)
659  {
660  for (i = j - 1; i >= 0; i--)
661  {
662  if (result._mantissa[i] != 0)
663  {
664  result._mantissa[i]--;
665  break;
666  }
667  result._mantissa[i] = 9;
668  }
669 
670  if (-1 == i)
671  {
672  error = nil;
673  }
674  }
675 
676  return error;
677 }
678 
688 function CPDecimalSubtract(result, leftOperand, rightOperand, roundingMode)
689 {
690  if (leftOperand._isNaN || rightOperand._isNaN)
691  {
692  result._isNaN = YES;
693  return CPCalculationNoError;
694  }
695 
696  // check for zero
697  if (CPDecimalIsZero(leftOperand))
698  {
699  _CPDecimalSet(result, rightOperand);
700  result._isNegative = !result._isNegative;
701  return CPCalculationNoError;
702  }
703 
704  if (CPDecimalIsZero(rightOperand))
705  {
706  _CPDecimalSet(result, leftOperand);
707  return CPCalculationNoError;
708  }
709 
710  var n1 = CPDecimalCopy(leftOperand),
711  n2 = CPDecimalCopy(rightOperand),
712  error1 = CPCalculationNoError;
713 
714  // For different signs use addition
715  if (leftOperand._isNegative != rightOperand._isNegative)
716  {
717  if (leftOperand._isNegative)
718  {
719  n1._isNegative = NO;
720  error1 = CPDecimalAdd(result, n1, rightOperand, roundingMode);
721  result._isNegative = YES;
722 
723  if (error1 == CPCalculationUnderflow)
724  error1 = CPCalculationOverflow;
725  else if (error1 == CPCalculationOverflow) // gnustep has bug here
726  error1 = CPCalculationUnderflow;
727 
728  return error1;
729  }
730  else
731  {
732  n2._isNegative = NO;
733  return CPDecimalAdd(result, leftOperand, n2, roundingMode);
734  }
735  }
736 
737  var error = CPDecimalNormalize(n1, n2, roundingMode),
738  comp = CPDecimalCompare(leftOperand, rightOperand);
739 
740  if (comp == CPOrderedSame)
741  {
742  _CPDecimalSetZero(result);
743  return CPCalculationNoError;
744  }
745 
746  // both negative, make positive and change order
747  if (leftOperand._isNegative)
748  {
749  n1._isNegative = NO;
750  n2._isNegative = NO;
751 
752  if (comp == CPOrderedAscending)
753  {
754  error1 = _SimpleSubtract(result, n1, n2, roundingMode);
755  result._isNegative = YES;
756  }
757  else
758  {
759  error1 = _SimpleSubtract(result, n2, n1, roundingMode);
760  }
761  }
762  else
763  {
764  if (comp == CPOrderedAscending)
765  {
766  error1 = _SimpleSubtract(result, n2, n1, roundingMode);
767  result._isNegative = YES;
768  }
769  else
770  {
771  error1 = _SimpleSubtract(result, n1, n2, roundingMode);
772  }
773  }
774 
775  CPDecimalCompact(result);
776 
777  if (error1 == CPCalculationNoError)
778  return error;
779  else
780  return error1;
781 }
782 
783 // this is a very simple O(n^2) implementation that uses subtract. Are there faster divides?
784 function _SimpleDivide(result, leftOperand, rightOperand, roundingMode)
785 {
786  var error = CPCalculationNoError,
787  n1 = CPDecimalMakeZero(),
788  k = 0,
789  firsttime = YES,
790  stopk = CPDecimalMaxDigits + 1,
791  used = 0; // How many digits of l have been used?
792 
793  _CPDecimalSetZero(result);
794 
795  n1._mantissa = [];
796 
797  while ((used < leftOperand._mantissa.length) || (n1._mantissa.length
798  && !((n1._mantissa.length == 1) && (n1._mantissa[0] == 0))))
799  {
800  while (CPOrderedAscending == CPDecimalCompare(n1, rightOperand))
801  {
802  if (stopk == k)
803  break;
804 
805  if (n1._exponent)
806  {
807  // Put back zeros removed by compacting
808  Array.prototype.push.call(n1._mantissa, 0);
809  n1._exponent--;
810  n1._isCompact = NO;
811  }
812  else
813  {
814  if (used < leftOperand._mantissa.length)
815  {
816  // Fill up with own digits
817  if (n1._mantissa.length || leftOperand._mantissa[used])
818  {
819  // only add 0 if there is already something
820  Array.prototype.push.call(n1._mantissa, (leftOperand._mantissa[used]));
821  n1._isCompact = NO;
822  }
823 
824  used++;
825  }
826  else
827  {
828  if (result._exponent == CPDecimalMinExponent)
829  {
830  // use this as an end flag
831  k = stopk;
832  break;
833  }
834 
835  // Borrow one digit
836  Array.prototype.push.call(n1._mantissa, 0);
837  result._exponent--;
838  }
839 
840  // Zeros must be added while enough digits are fetched to do the
841  // subtraction, but first time round this just add zeros at the
842  // start of the number , increases k, and hence reduces
843  // the available precision. To solve this only inc k/add zeros if
844  // this isn't first time round.
845  if (!firsttime)
846  {
847  k++;
848  result._mantissa[k - 1] = 0;
849  }
850  }
851  }
852 
853  // At this point digit in result we are working on is (k-1) so when
854  // k == (CPDecimalMaxDigits+1) then we should stop i.e. last subtract
855  // was last valid one.
856  if (stopk == k)
857  {
859  break;
860  }
861 
862  if (firsttime)
863  {
864  firsttime = NO;
865  k++;
866  }
867 
868  error1 = CPDecimalSubtract(n1, n1, rightOperand, roundingMode);
869 
870  if (error1 != CPCalculationNoError)
871  error = error1;
872 
873  result._mantissa[k - 1]++;
874  }
875 
876  return error;
877 }
878 
888 function CPDecimalDivide(result, leftOperand, rightOperand, roundingMode)
889 {
890  var error = CPCalculationNoError,
891  exp = leftOperand._exponent - rightOperand._exponent,
892  neg = (leftOperand._isNegative != rightOperand._isNegative);
893 
894  if (leftOperand._isNaN || rightOperand._isNaN)
895  {
896  result._isNaN = YES;
897  return CPCalculationNoError;
898  }
899 
900  // check for zero
901  if (CPDecimalIsZero(rightOperand))
902  {
903  result._isNaN = YES;
905  }
906 
907  if (CPDecimalIsZero(leftOperand))
908  {
909  _CPDecimalSetZero(result);
910  return CPCalculationNoError;
911  }
912 
913  //FIXME: Should also check for one
914 
915  var n1 = CPDecimalCopy(leftOperand),
916  n2 = CPDecimalCopy(rightOperand);
917 
918  n1._exponent = 0;
919  n1._isNegative = NO;
920  n2._exponent = 0;
921  n2._isNegative = NO;
922 
923  error = _SimpleDivide(result, n1, n2, roundingMode);
924  CPDecimalCompact(result);
925 
926  if (result._exponent + exp > CPDecimalMaxExponent)
927  {
928  result._isNaN = YES;
929  if (neg)
930  return CPCalculationUnderflow;
931  else
932  return CPCalculationOverflow;
933  }
934  else if (result._exponent + exp < CPDecimalMinExponent)
935  {
936  // We must cut off some digits
937  CPDecimalRound(result, result, exp + CPDecimalMaxExponent + 1, roundingMode);
939 
940  if (result._exponent + exp < CPDecimalMinExponent)
941  {
942  CPDecimalSetZero(result);
943  return error;
944  }
945  }
946 
947  result._exponent += exp;
948  result._isNegative = neg;
949  return error;
950 }
951 
952 // Simple multiply O(n^2) , replace with something faster, like divide-n-conquer algo?
953 function _SimpleMultiply(result, leftOperand, rightOperand, roundingMode, powerMode)
954 {
955  var error = CPCalculationNoError,
956  carry = 0,
957  exp = 0,
958  n = CPDecimalMakeZero();
959 
960  _CPDecimalSetZero(result);
961 
962  // Do every digit of the second number
963  for (var i = 0; i < rightOperand._mantissa.length; i++)
964  {
965  _CPDecimalSetZero(n);
966 
967  n._exponent = rightOperand._mantissa.length - i - 1;
968  carry = 0;
969  d = rightOperand._mantissa[i];
970 
971  if (d == 0)
972  continue;
973 
974  for (var j = leftOperand._mantissa.length - 1; j >= 0; j--)
975  {
976  e = leftOperand._mantissa[j] * d + carry;
977 
978  if (e >= 10)
979  {
980  carry = FLOOR(e / 10);
981  e = e % 10;
982  }
983  else
984  carry = 0;
985 
986  // This is one off to allow final carry
987  n._mantissa[j + 1] = e;
988  }
989 
990  n._mantissa[0] = carry;
991 
992  CPDecimalCompact(n);
993 
994  error1 = CPDecimalAdd(result, result, n, roundingMode, YES);
995 
996  if (error1 != CPCalculationNoError)
997  error = error1;
998  }
999 
1000  if (result._exponent + exp > CPDecimalMaxExponent)
1001  {
1002  // This should almost never happen
1003  result._isNaN = YES;
1004  return CPCalculationOverflow;
1005  }
1006 
1007  result._exponent += exp;
1008 
1009  // perform round to CPDecimalMaxDigits
1010  if (result._mantissa.length > CPDecimalMaxDigits && !powerMode)
1011  {
1012  result._isCompact = NO;
1013 
1014  var scale = CPDecimalMaxDigits - (result._mantissa.length + result._exponent);
1015  CPDecimalRound(result, result, scale, roundingMode); // calls compact
1016 
1018  }
1019 
1020  return error;
1021 }
1022 
1032 function CPDecimalMultiply(result, leftOperand, rightOperand, roundingMode, powerMode)
1033 {
1034  var error = CPCalculationNoError,
1035  exp = leftOperand._exponent + rightOperand._exponent,
1036  neg = (leftOperand._isNegative != rightOperand._isNegative);
1037 
1038  if (leftOperand._isNaN || rightOperand._isNaN)
1039  {
1040  result._isNaN = YES;
1041  return CPCalculationNoError;
1042  }
1043 
1044  // check for zero
1045  if (CPDecimalIsZero(rightOperand) || CPDecimalIsZero(leftOperand))
1046  {
1047  _CPDecimalSetZero(result);
1048  return CPCalculationNoError;
1049  }
1050 
1051  //FIXME: Should also check for one
1052 
1053  if (exp > CPDecimalMaxExponent)
1054  {
1055  result._isNaN = YES;
1056 
1057  if (neg)
1058  return CPCalculationUnderflow;
1059  else
1060  return CPCalculationOverflow;
1061  }
1062 
1063  var n1 = CPDecimalCopy(leftOperand),
1064  n2 = CPDecimalCopy(rightOperand);
1065 
1066  n1._exponent = 0;
1067  n2._exponent = 0;
1068  n1._isNegative = NO;
1069  n2._isNegative = NO;
1070 
1071  // below is equiv. of simple compare
1072  var comp = 0,
1073  ll = n1._mantissa.length,
1074  lr = n2._mantissa.length;
1075 
1076  if (ll == lr)
1077  comp = CPOrderedSame;
1078  else if (ll > lr)
1079  comp = CPOrderedDescending;
1080  else
1081  comp = CPOrderedAscending;
1082 
1083  if (comp == CPOrderedDescending)
1084  {
1085  error = _SimpleMultiply(result, n1, n2, roundingMode, powerMode);
1086  }
1087  else
1088  {
1089  error = _SimpleMultiply(result, n2, n1, roundingMode, powerMode);
1090  }
1091 
1092  CPDecimalCompact(result);
1093 
1094  if (result._exponent + exp > CPDecimalMaxExponent)
1095  {
1096  result._isNaN = YES;
1097 
1098  if (neg)
1099  return CPCalculationUnderflow;
1100  else
1101  return CPCalculationOverflow;
1102  }
1103  else if (result._exponent + exp < CPDecimalMinExponent)
1104  {
1105  // We must cut off some digits
1106  CPDecimalRound(result, result, exp + CPDecimalMaxExponent + 1, roundingMode);
1108 
1109  if (result._exponent + exp < CPDecimalMinExponent)
1110  {
1111  _CPDecimalSetZero(result);
1112  return error;
1113  }
1114  }
1115 
1116  result._exponent += exp;
1117  result._isNegative = neg;
1118 
1119  return error;
1120 }
1121 
1131 function CPDecimalMultiplyByPowerOf10(result, dcm, power, roundingMode)
1132 {
1133  _CPDecimalSet(result, dcm);
1134 
1135  var p = result._exponent + power;
1136 
1137  if (p > CPDecimalMaxExponent)
1138  {
1139  result._isNaN = YES;
1140  return CPCalculationOverflow;
1141  }
1142 
1143  if (p < CPDecimalMinExponent)
1144  {
1145  result._isNaN = YES;
1146  return CPCalculationUnderflow;
1147  }
1148 
1149  result._exponent += power;
1150  return CPCalculationNoError;
1151 }
1152 
1162 function CPDecimalPower(result, dcm, power, roundingMode)
1163 {
1164  var error = CPCalculationNoError,
1165  neg = (dcm._isNegative && (power % 2)),
1166  n1 = CPDecimalCopy(dcm);
1167 
1168  n1._isNegative = NO;
1169 
1170  _CPDecimalSetOne(result);
1171 
1172  var e = power;
1173 
1174  while (e)
1175  {
1176  if (e & 1)
1177  {
1178  error = CPDecimalMultiply(result, result, n1, roundingMode); //, YES); // enable for high precision powers
1179  }
1180 
1181  error = CPDecimalMultiply(n1, n1, n1, roundingMode); //, YES); // enable for high precision powers
1182 
1183  e >>= 1;
1184 
1185  if (error > CPCalculationLossOfPrecision)
1186  break;
1187  }
1188 
1189  result._isNegative = neg;
1190 
1191 /* // enable is powerMode to do finally rounding to Max Digits.
1192  if ([result._mantissa count] > CPDecimalMaxDigits)
1193  {
1194  result._isCompact = NO;
1195  var scale = CPDecimalMaxDigits - ([result._mantissa count] + result._exponent);
1196  CPDecimalRound(result, result, scale ,roundingMode); // calls compact
1197  error = CPCalculationLossOfPrecision;
1198  }
1199 */
1200 
1201  CPDecimalCompact(result);
1202 
1203  return error;
1204 }
1205 
1215 function CPDecimalNormalize(dcm1, dcm2, roundingMode, longMode)
1216 {
1217  var factor = (longMode) ? 2 : 1;
1218 
1219  if (dcm1._isNaN || dcm2._isNaN)
1220  return CPCalculationNoError; // FIXME: correct behavior?
1221 
1222  // ensure compact
1223  if (!dcm1._isCompact)
1224  CPDecimalCompact(dcm1);
1225 
1226  if (!dcm2._isCompact)
1227  CPDecimalCompact(dcm2);
1228 
1229  if (dcm1._exponent == dcm2._exponent)
1230  return CPCalculationNoError;
1231 
1232  var e1 = dcm1._exponent,
1233  e2 = dcm2._exponent;
1234 
1235  // Add zeros
1236  var l2 = dcm2._mantissa.length,
1237  l1 = dcm1._mantissa.length,
1238  l = 0;
1239 
1240  var e = 0;
1241 
1242  if (e2 > e1 && e1 >= 0 && e2 >= 0)
1243  e = e2 - e1;
1244  else if (e2 > e1 && e1 < 0 && e2 >= 0)
1245  e = e2 - e1;
1246  else if (e2 > e1 && e1 < 0 && e2 < 0)
1247  e = e2 - e1;
1248  else if (e2 < e1 && e1 >= 0 && e2 >= 0)
1249  e = e1 - e2;
1250  else if (e2 < e1 && e1 >= 0 && e2 < 0)
1251  e = e1 - e2;
1252  else if (e2 < e1 && e1 < 0 && e2 < 0)
1253  e = e1 - e2;
1254 
1255  if (e2 > e1)
1256  l = MIN((CPDecimalMaxDigits * factor) - l2, e); //(e2 - e1));
1257  else
1258  l = MIN((CPDecimalMaxDigits * factor) - l1, e); //(e1 - e2));
1259 
1260  for (var i = 0; i < l; i++)
1261  {
1262  if (e2 > e1)
1263  Array.prototype.push.call(dcm2._mantissa, 0); //dcm2._mantissa[i + l2] = 0;
1264  else
1265  Array.prototype.push.call(dcm1._mantissa, 0);
1266  }
1267 
1268  if (e2 > e1)
1269  {
1270  dcm2._exponent -= l;
1271  dcm2._isCompact = NO;
1272  }
1273  else
1274  {
1275  dcm1._exponent -= l;
1276  dcm1._isCompact = NO;
1277  }
1278 
1279  // has been normalised?
1280  if (l != ABS(e2 - e1))//e2 - e1)
1281  {
1282  // no..
1283  // Round of some digits to increase exponent - will compact too
1284  // One number may become zero after this
1285  if (e2 > e1)
1286  {
1287  CPDecimalRound(dcm1, dcm1, -dcm2._exponent, roundingMode);
1288  l1 = CPDecimalIsZero(dcm1);
1289  }
1290  else
1291  {
1292  CPDecimalRound(dcm2, dcm2, -dcm1._exponent, roundingMode);
1293  l2 = CPDecimalIsZero(dcm2);
1294  }
1295 
1296  if ((dcm1._exponent != dcm2._exponent) && ((!l1) || (!l2)))
1297  {
1298  // Some zeros where cut of again by compacting
1299  if (e2 > e1)
1300  {
1301  l1 = dcm1._mantissa.length;
1302  l = MIN((CPDecimalMaxDigits * factor) - l1, ABS(dcm1._exponent - dcm2._exponent));
1303  for (var i = 0; i < l; i++)
1304  {
1305  dcm1._mantissa[i + l1] = 0; // or addObject: ? one faster than other?
1306  }
1307  dcm1._isCompact = NO;
1308  dcm1._exponent = dcm2._exponent;
1309  }
1310  else
1311  {
1312  l2 = dcm2._mantissa.length;
1313  l = MIN((CPDecimalMaxDigits * factor) - l2, ABS(dcm2._exponent - dcm1._exponent));
1314  for (var i = 0; i < l; i++)
1315  {
1316  dcm2._mantissa[i + l2] = 0; // or addObject: ? one faster than other?
1317  }
1318  dcm2._exponent = dcm1._exponent;
1319  dcm2._isCompact = NO;
1320  }
1321  }
1322 
1324  }
1325 
1326  return CPCalculationNoError;
1327 }
1328 
1341 function CPDecimalRound(result, dcm, scale, roundingMode)
1342 {
1343  _CPDecimalSet(result, dcm);
1344 
1345  if (dcm._isNaN)
1346  return;
1347 
1348  if (!dcm._isCompact)
1349  CPDecimalCompact(dcm);
1350 
1351  // FIXME: check for valid inputs (eg scale etc)
1352 
1353  // FIXME: if in longMode should this double?
1354  if (scale == CPDecimalNoScale)
1355  return;
1356 
1357  var mc = result._mantissa.length,
1358  l = mc + scale + result._exponent;
1359 
1360  if (mc <= l)
1361  return;
1362 
1363  else if (l <= 0)
1364  {
1365  _CPDecimalSetZero(result);
1366  return;
1367  }
1368  else
1369  {
1370  var c = 0,
1371  n = 0,
1372  up = 0;
1373 
1374  // Adjust length and exponent
1375  result._exponent += mc - l;
1376 
1377  switch (roundingMode)
1378  {
1379  case CPRoundDown:
1380  up = result._isNegative;
1381  break;
1382 
1383  case CPRoundUp:
1384  up = !result._isNegative;
1385  break;
1386 
1387  case CPRoundPlain:
1388  n = result._mantissa[l];
1389  up = (n >= 5);
1390  break;
1391 
1392  case _CPRoundHalfDown:
1393  n = result._mantissa[l];
1394  up = (n > 5);
1395  break;
1396 
1397  case CPRoundBankers:
1398  n = result._mantissa[l];
1399 
1400  if (n > 5)
1401  up = YES;
1402  else if (n < 5)
1403  up = NO;
1404  else
1405  {
1406  if (l == 0)
1407  c = 0;
1408  else
1409  c = result._mantissa[l - 1];
1410  up = ((c % 2) != 0);
1411  }
1412  break;
1413 
1414  default:
1415  up = NO;
1416  break;
1417  }
1418 
1419  // cut mantissa
1420  result._mantissa = Array.prototype.slice.call(result._mantissa, 0, l);
1421 
1422  if (up)
1423  {
1424  for (var i = l-1; i >= 0; i--)
1425  {
1426  if (result._mantissa[i] != 9)
1427  {
1428  result._mantissa[i]++;
1429  break;
1430  }
1431 
1432  result._mantissa[i] = 0;
1433  }
1434 
1435  // Final overflow?
1436  if (i == -1)
1437  {
1438  // As all digits are zeros, just change the first
1439  result._mantissa[0] = 1;
1440 
1441  if (result._exponent >= CPDecimalMaxExponent)
1442  {
1443  // Overflow in rounding.
1444  // Add one zero add the end. There must be space as
1445  // we just cut off some digits.
1446  Array.prototype.push.call(result._mantissa, 0);
1447  }
1448  else
1449  result._exponent++;
1450  }
1451  }
1452  }
1453 
1454  CPDecimalCompact(result);
1455 }
1456 
1462 function CPDecimalCompact(dcm)
1463 {
1464  // if positive or zero exp leading zeros simply delete, trailing ones u need to increment exponent
1465  if (!dcm || dcm._mantissa.length == 0 || CPDecimalIsNotANumber(dcm))
1466  return;
1467 
1468  if (CPDecimalIsZero(dcm))
1469  {
1470  // handle zero number compacting
1471  _CPDecimalSetZero(dcm);
1472  return;
1473  }
1474 
1475  // leading zeros, when exponent is zero these mean we need to move our decimal point to compact
1476  // if exp is zero does it make sense to have them? don't think so so delete them
1477  while (dcm._mantissa[0] === 0)
1478  Array.prototype.shift.call(dcm._mantissa);
1479 
1480  // trailing zeros, strip them
1481  while (dcm._mantissa[dcm._mantissa.length - 1] === 0)
1482  {
1483  Array.prototype.pop.call(dcm._mantissa);
1484  dcm._exponent++;
1485 
1486  if (dcm._exponent + 1 > CPDecimalMaxExponent)
1487  {
1488  // TODO: test case for this
1489  // overflow if we compact anymore, so don't
1490  break;
1491  }
1492  }
1493 
1494  dcm._isCompact = YES;
1495 }
1496 
1504 function CPDecimalString(dcm, locale)
1505 {
1506  // Cocoa seems to just add all the zeros... this maybe controlled by locale,
1507  // will check.
1508  if (dcm._isNaN)
1509  return @"NaN";
1510 
1511  var string = @"",
1512  i = 0;
1513 
1514  if (dcm._isNegative)
1515  string += "-";
1516 
1517  var k = dcm._mantissa.length,
1518  l = ((dcm._exponent < 0) ? dcm._exponent : 0) + k;
1519 
1520  if (l < 0)
1521  {
1522  // add leading zeros
1523  string += "0.";
1524  for (i = 0; i < ABS(l); i++)
1525  {
1526  string += "0";
1527  }
1528  l = k;
1529  }
1530  else if (l == 0)
1531  {
1532  string += "0";
1533  }
1534 
1535  for (i = 0; i < l; i++)
1536  {
1537  string += dcm._mantissa[i];
1538  }
1539 
1540  if (l < k)
1541  {
1542  string += ".";
1543  for (i = l; i < k; i++)
1544  {
1545  string += dcm._mantissa[i];
1546  }
1547  }
1548 
1549  for (i = 0; i < dcm._exponent; i++)
1550  {
1551  string += "0";
1552  }
1553 
1554  return string;
1555  /*
1556  // GNUStep
1557  if (dcm._isNaN)
1558  return @"NaN";
1559 
1560  var sep = 0;
1561  if ((locale == nil) || (sep = [locale objectForKey: CPDecimalSeparator]) == nil)
1562  sep = @".";
1563 
1564  if (CPDecimalIsZero(dcm))
1565  return @"0" + sep + "0";
1566 
1567  var string = @"";
1568 
1569  if (dcm._isNegative)
1570  string += "-";
1571 
1572  var len = [dcm._mantissa count],
1573  size = len + dcm._exponent;
1574 
1575  if ((len <= 6) && (0 < size) && (size < 7))
1576  {
1577  // For small numbers use the normal format
1578  var i = 0
1579  for (; i < len; i++)
1580  {
1581  if (size == i)
1582  string += sep;
1583  d = dcm._mantissa[i];
1584  string += d.toString();
1585  }
1586  for (i = 0; i < dcm._exponent; i++)
1587  {
1588  string += "0";
1589  }
1590  }
1591  else if ((len <= 6) && (0 >= size) && (size > -3))
1592  {
1593  // For small numbers use the normal format
1594  string += "0";
1595  string += sep;
1596 
1597  var i = 0;
1598  for (; i > size; i--)
1599  {
1600  string += "0";
1601  }
1602  for (i = 0; i < len; i++)
1603  {
1604  d = dcm._mantissa[i];
1605  string += d.toString();
1606  }
1607  }
1608  else
1609  {
1610  // Scientific format
1611  var i = 0;
1612  for (; i < len; i++)
1613  {
1614  if (1 == i)
1615  string += sep;
1616  d = dcm._mantissa[i];
1617  string += d.toString();
1618  }
1619  if (size != 1)
1620  {
1621  //s = [NSString stringWithFormat: @"E%d", size-1];
1622  //[string appendString: s];
1623  string += "E" + (size - 1).toString();
1624  }
1625  }
1626 
1627  return string;
1628  */
1629 }
function CPDecimalMakeZero()
Definition: CPDecimal.j:229
function CPDecimalMakeOne()
Definition: CPDecimal.j:239
CPCalculationUnderflow
Definition: CPDecimal.j:67
function CPDecimalIsNotANumber(dcm)
Definition: CPDecimal.j:350
function CPDecimalRound(result, dcm, scale, roundingMode)
Definition: CPDecimal.j:1341
CPOrderedAscending
Definition: CPObjJRuntime.j:48
function CPDecimalMultiply(result, leftOperand, rightOperand, roundingMode, powerMode)
Definition: CPDecimal.j:1032
function CPDecimalMakeWithString(string, locale)
Definition: CPDecimal.j:104
CPRoundBankers
Definition: CPDecimal.j:75
CPOrderedSame
Definition: CPObjJRuntime.j:54
function CPDecimalNormalize(dcm1, dcm2, roundingMode, longMode)
Definition: CPDecimal.j:1215
CPDecimalNumberUnderflowException
Definition: CPDecimal.j:80
function CPDecimalCompact(dcm)
Definition: CPDecimal.j:1462
function CPDecimalSubtract(result, leftOperand, rightOperand, roundingMode)
Definition: CPDecimal.j:688
function CPDecimalIsOne(dcm)
Definition: CPDecimal.j:303
function CPDecimalIsZero(dcm)
Definition: CPDecimal.j:283
function CPDecimalCopy(dcm)
Definition: CPDecimal.j:361
function CPDecimalString(dcm, locale)
Definition: CPDecimal.j:1504
function CPDecimalMultiplyByPowerOf10(result, dcm, power, roundingMode)
Definition: CPDecimal.j:1131
CPCalculationDivideByZero
Definition: CPDecimal.j:68
CPOrderedDescending
Definition: CPObjJRuntime.j:60
CPDecimalNoScale
Definition: CPDecimal.j:61
function CPDecimalMakeNaN()
Definition: CPDecimal.j:250
CPCalculationLossOfPrecision
Definition: CPDecimal.j:65
function CPDecimalPower(result, dcm, power, roundingMode)
Definition: CPDecimal.j:1162
CPRoundDown
Definition: CPDecimal.j:73
function CPDecimalAdd(result, leftOperand, rightOperand, roundingMode, longMode)
Definition: CPDecimal.j:534
function CPDecimalCompare(leftOperand, rightOperand)
Definition: CPDecimal.j:379
CPDecimalNumberExactnessException
Definition: CPDecimal.j:81
CPRoundingMode CPRoundPlain
Definition: CPDecimal.j:72
CPDecimal CPDecimalMaxDigits
Definition: CPDecimal.j:56
CPCalculationOverflow
Definition: CPDecimal.j:66
function CPDecimalMakeWithParts(mantissa, exponent)
Definition: CPDecimal.j:193
function CPDecimalDivide(result, leftOperand, rightOperand, roundingMode)
Definition: CPDecimal.j:888
CPRoundUp
Definition: CPDecimal.j:74
CPDecimalMinExponent
Definition: CPDecimal.j:58
CPDecimalMaxExponent
Definition: CPDecimal.j:57
CPDecimalNumberOverflowException
Definition: CPDecimal.j:79
CPDecimalNumberDivideByZeroException
Definition: CPDecimal.j:82