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