API  0.9.6
 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|[1-9]\d*)) - integer part, cant have leading zeros
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|[1-9]\d*)?)(?:\.(\d*))?(?:[eE]([+\-]?)(\d+))?$/);
118  if (!matches)
119  return CPDecimalMakeNaN();
120 
121  var ds = matches[1],
122  intpart = matches[2],
123  decpart = matches[3],
124  es = matches[4],
125  exp = matches[5];
126 
127  var isNegative = NO;
128  if (ds && ds === "-")
129  isNegative = YES;
130 
131  var exponent = 0;
132  if (exp)
133  exponent = parseInt(exp) * ((es && es === "-")?-1:1);
134 
135  if (decpart) // push decimal point to last digit, then let compact handle the zeros
136  exponent -= decpart.length;
137 
138  var inputlength = (intpart?intpart.length:0) + (decpart?decpart.length:0);
139  if (inputlength > CPDecimalMaxDigits)
140  {
141  // input is too long, increase exponent and truncate
142  exponent += inputlength - CPDecimalMaxDigits;
143  }
144  else if (inputlength === 0)
145  {
146  return CPDecimalMakeNaN();
147  }
148 
149  if (exponent > CPDecimalMaxExponent || exponent < CPDecimalMinExponent)
150  return CPDecimalMakeNaN();
151 
152  // Representation internally starts at most significant digit
153  var m = [],
154  i = 0;
155  for (; i < (intpart?intpart.length:0); i++)
156  {
157  if (i >= CPDecimalMaxDigits)
158  break; // truncate
159  Array.prototype.push.call(m, parseInt(intpart.charAt(i)));
160  }
161  var j = 0;
162  for (; j < (decpart?decpart.length:0); j++)
163  {
164  if ((i + j) >= CPDecimalMaxDigits)
165  break; // truncate
166  Array.prototype.push.call(m, parseInt(decpart.charAt(j)));
167  }
168 
169  var dcm = {_exponent:exponent, _isNegative:isNegative, _isCompact:NO, _isNaN:NO, _mantissa:m};
170  CPDecimalCompact(dcm);
171 
172  return dcm;
173 }
174 
182 function CPDecimalMakeWithParts(mantissa, exponent)
183 {
184  var m = [],
185  isNegative = NO;
186 
187  if (mantissa < 0)
188  {
189  isNegative = YES;
190  mantissa = ABS(mantissa);
191  }
192 
193  if (mantissa == 0)
194  Array.prototype.push.call(m, 0);
195 
196  if (exponent > CPDecimalMaxExponent || exponent < CPDecimalMinExponent)
197  return CPDecimalMakeNaN();
198 
199  // remaining digits are disposed of via truncation
200  while ((mantissa > 0) && (m.length < CPDecimalMaxDigits)) // count selector here could be optimised away
201  {
202  Array.prototype.unshift.call(m, parseInt(mantissa % 10));
203  mantissa = FLOOR(mantissa / 10);
204  }
205 
206  var dcm = {_exponent:exponent, _isNegative:isNegative, _isCompact:YES, _isNaN:NO, _mantissa:m};
207 
208  CPDecimalCompact(dcm);
209 
210  return dcm;
211 }
212 
219 {
220  return CPDecimalMakeWithParts(0, 0);
221 }
222 
229 {
230  return CPDecimalMakeWithParts(1, 0);
231 }
232 
233 
240 {
241  var d = CPDecimalMakeWithParts(0, 0);
242  d._isNaN = YES;
243  return d;
244 }
245 
246 // private methods
247 function _CPDecimalMakeMaximum()
248 {
249  var s = @"",
250  i = 0;
251  for (; i < CPDecimalMaxDigits; i++)
252  s += "9";
253  s += "e" + CPDecimalMaxExponent;
254  return CPDecimalMakeWithString(s);
255 }
256 
257 function _CPDecimalMakeMinimum()
258 {
259  var s = @"-",
260  i = 0;
261  for (; i < CPDecimalMaxDigits; i++)
262  s += "9";
263  s += "e" + CPDecimalMaxExponent;
264  return CPDecimalMakeWithString(s);
265 }
266 
272 function CPDecimalIsZero(dcm)
273 {
274  // exponent doesn't matter as long as mantissa = 0
275  if (!dcm._isNaN)
276  {
277  for (var i = 0; i < dcm._mantissa.length; i++)
278  if (dcm._mantissa[i] !== 0)
279  return NO;
280 
281  return YES;
282  }
283 
284  return NO;
285 }
286 
292 function CPDecimalIsOne(dcm)
293 {
294  CPDecimalCompact(dcm);
295  // exponent doesn't matter as long as mantissa = 0
296  if (!dcm._isNaN)
297  {
298  if (dcm._mantissa && (dcm._mantissa.length == 1) && (dcm._mantissa[0] == 1))
299  return YES;
300  }
301  return NO;
302 }
303 
304 //private method to copy attribute values
305 function _CPDecimalSet(t,s)
306 {
307  // should all be [x copy] ?
308  t._exponent = s._exponent;
309  t._isNegative = s._isNegative;
310  t._isCompact = s._isCompact;
311  t._isNaN = s._isNaN;
312  t._mantissa = Array.prototype.slice.call(s._mantissa, 0);
313 }
314 
315 function _CPDecimalSetZero(result)
316 {
317  result._mantissa = [0];
318  result._exponent = 0;
319  result._isNegative = NO;
320  result._isCompact = YES;
321  result._isNaN = NO;
322 }
323 
324 function _CPDecimalSetOne(result)
325 {
326  result._mantissa = [1];
327  result._exponent = 0;
328  result._isNegative = NO;
329  result._isCompact = YES;
330  result._isNaN = NO;
331 }
332 
339 {
340  return (dcm._isNaN)?YES:NO;
341 }
342 
349 function CPDecimalCopy(dcm)
350 {
351  return {_exponent:dcm._exponent,
352  _isNegative:dcm._isNegative,
353  _isCompact:dcm._isCompact,
354  _isNaN:dcm._isNaN,
355  _mantissa:Array.prototype.slice.call(dcm._mantissa, 0)
356  };
357 }
358 
367 function CPDecimalCompare(leftOperand, rightOperand)
368 {
369  if (leftOperand._isNaN && rightOperand._isNaN)
370  return CPOrderedSame;
371 
372  if (leftOperand._isNegative != rightOperand._isNegative)
373  {
374  if (rightOperand._isNegative)
375  return CPOrderedDescending;
376  else
377  return CPOrderedAscending;
378  }
379 
380  // Before comparing number size check if zero (dont use CPDecimalIsZero as it is more computationally intensive)
381  var leftIsZero = (leftOperand._mantissa.length == 1 && leftOperand._mantissa[0] == 0),
382  rightIsZero = (rightOperand._mantissa.length == 1 && rightOperand._mantissa[0] == 0),
383  // Sign is the same, quick check size (length + exp)
384  s1 = leftOperand._exponent + leftOperand._mantissa.length,
385  s2 = rightOperand._exponent + rightOperand._mantissa.length;
386 
387  if (leftIsZero || s1 < s2)
388  {
389  if (rightOperand._isNegative)
390  return CPOrderedDescending;
391  else
392  return CPOrderedAscending;
393  }
394  if (rightIsZero || s1 > s2)
395  {
396  if (rightOperand._isNegative)
397  return CPOrderedAscending;
398  else
399  return CPOrderedDescending;
400  }
401 
402  // Same size, so check mantissa
403  var l = MIN(leftOperand._mantissa.length, rightOperand._mantissa.length),
404  i = 0;
405 
406  for (; i < l; i++)
407  {
408  var d = rightOperand._mantissa[i] - leftOperand._mantissa[i];
409 
410  if (d > 0)
411  {
412  if (rightOperand._isNegative)
413  return CPOrderedDescending;
414  else
415  return CPOrderedAscending;
416  }
417  if (d < 0)
418  {
419  if (rightOperand._isNegative)
420  return CPOrderedAscending;
421  else
422  return CPOrderedDescending;
423  }
424  }
425 
426  // Same digits, check length
427  if (leftOperand._mantissa.length > rightOperand._mantissa.length)
428  {
429  if (rightOperand._isNegative)
430  return CPOrderedAscending;
431  else
432  return CPOrderedDescending;
433  }
434  if (leftOperand._mantissa.length < rightOperand._mantissa.length)
435  {
436  if (rightOperand._isNegative)
437  return CPOrderedDescending;
438  else
439  return CPOrderedAscending;
440  }
441 
442  return CPOrderedSame;
443 }
444 
445 // GNUSteps addition. This is standard O(n) complexity.
446 // longMode makes the addition not round for up to double max digits, this is
447 // to preserve precision during multiplication
448 function _SimpleAdd(result, leftOperand, rightOperand, roundingMode, longMode)
449 {
450  var factor = (longMode)?2:1;
451 
452  _CPDecimalSet(result, leftOperand);
453 
454  var j = leftOperand._mantissa.length - rightOperand._mantissa.length,
455  l = rightOperand._mantissa.length,
456  i = l - 1,
457  carry = 0,
458  error = CPCalculationNoError;
459 
460  // Add all the digits
461  for (; i >= 0; i--)
462  {
463  var d = rightOperand._mantissa[i] + result._mantissa[i + j] + carry;
464  if (d >= 10)
465  {
466  d = d % 10; // a division. subtraction and conditions faster?
467  carry = 1;
468  }
469  else
470  carry = 0;
471 
472  result._mantissa[i + j] = d;
473  }
474 
475  if (carry)
476  {
477  for (i = j - 1; i >= 0; i--)
478  {
479  if (result._mantissa[i] != 9)
480  {
481  result._mantissa[i]++;
482  carry = 0;
483  break;
484  }
485  result._mantissa[i] = 0;
486  }
487 
488  if (carry)
489  {
490  Array.prototype.splice.call(result._mantissa, 0, 0, 1);
491 
492  // The number must be shifted to the right
493  if ((CPDecimalMaxDigits * factor) == leftOperand._mantissa.length)
494  {
495  var scale = - result._exponent - 1;
496  CPDecimalRound(result, result, scale, roundingMode);
497  }
498 
499  if (CPDecimalMaxExponent < result._exponent)
500  {
501  result._isNaN = YES;
502  error = CPCalculationOverflow;
503  result._exponent = CPDecimalMaxExponent;
504  }
505  }
506  }
507  return error;
508 }
509 
519 function CPDecimalAdd(result, leftOperand, rightOperand, roundingMode, longMode)
520 {
521  if (leftOperand._isNaN || rightOperand._isNaN)
522  {
523  result._isNaN = YES;
524  return CPCalculationNoError;
525  }
526  // check for zero
527  if (CPDecimalIsZero(leftOperand))
528  {
529  _CPDecimalSet(result, rightOperand);
530  return CPCalculationNoError;
531  }
532  if (CPDecimalIsZero(rightOperand))
533  {
534  _CPDecimalSet(result, leftOperand);
535  return CPCalculationNoError;
536  }
537 
538  var n1 = CPDecimalCopy(leftOperand),
539  n2 = CPDecimalCopy(rightOperand);
540 
541  // For different signs use subtraction
542  if (leftOperand._isNegative != rightOperand._isNegative)
543  {
544  if (leftOperand._isNegative)
545  {
546  n1._isNegative = NO;
547  return CPDecimalSubtract(result, rightOperand, n1, roundingMode);
548  }
549  else
550  {
551  n2._isNegative = NO;
552  return CPDecimalSubtract(result, leftOperand, n2, roundingMode);
553  }
554  }
555 
556  var normerror = CPDecimalNormalize(n1, n2, roundingMode, longMode);
557 
558  // below is equiv. of simple compare
559  var comp = 0,
560  ll = n1._mantissa.length,
561  lr = n2._mantissa.length;
562  if (ll == lr)
563  comp = CPOrderedSame;
564  else if (ll > lr)
565  comp = CPOrderedDescending;
566  else
567  comp = CPOrderedAscending;
568 
569  // both negative, make positive
570  if (leftOperand._isNegative)
571  {
572  n1._isNegative = NO;
573  n2._isNegative = NO;
574  // SimpleCompare does not look at sign
575  if (comp == CPOrderedDescending)
576  {
577  adderror = _SimpleAdd(result, n1, n2, roundingMode, longMode);
578  }
579  else
580  {
581  adderror = _SimpleAdd(result, n2, n1, roundingMode, longMode);
582  }
583  result._isNegative = YES;
584  // swap sign over over/underflow exception
585  if (CPCalculationUnderflow == adderror)
586  adderror = CPCalculationOverflow;
587  else if (CPCalculationUnderflow == adderror)
588  adderror = CPCalculationUnderflow;
589  }
590  else
591  {
592  if (comp == CPOrderedAscending)
593  {
594  adderror = _SimpleAdd(result, n2, n1, roundingMode, longMode);
595  }
596  else
597  {
598  adderror = _SimpleAdd(result, n1, n2, roundingMode, longMode);
599  }
600  }
601 
602  CPDecimalCompact(result);
603 
604  if (adderror == CPCalculationNoError)
605  return normerror;
606  else
607  return adderror;
608 }
609 
610 // GNUStep port internal subtract
611 function _SimpleSubtract(result, leftOperand, rightOperand, roundingMode)
612 {
613  var error = CPCalculationNoError,
614  borrow = 0,
615  l = rightOperand._mantissa.length,
616  j = leftOperand._mantissa.length - l,
617  i = l - 1;
618 
619  _CPDecimalSet(result, leftOperand);
620 
621  // Now subtract all digits
622  for (; i >= 0; i--)
623  {
624  var d = result._mantissa[i + j] - rightOperand._mantissa[i] - borrow;
625  if (d < 0)
626  {
627  d = d + 10;
628  borrow = 1;
629  }
630  else
631  borrow = 0;
632 
633  result._mantissa[i + j] = d;
634  }
635 
636  if (borrow)
637  {
638  for (i = j - 1; i >= 0; i--)
639  {
640  if (result._mantissa[i] != 0)
641  {
642  result._mantissa[i]--;
643  break;
644  }
645  result._mantissa[i] = 9;
646  }
647 
648  if (-1 == i)
649  {
650  error = nil;
651  }
652  }
653 
654  return error;
655 }
656 
666 function CPDecimalSubtract(result, leftOperand, rightOperand, roundingMode)
667 {
668  if (leftOperand._isNaN || rightOperand._isNaN)
669  {
670  result._isNaN = YES;
671  return CPCalculationNoError;
672  }
673 
674  // check for zero
675  if (CPDecimalIsZero(leftOperand))
676  {
677  _CPDecimalSet(result, rightOperand);
678  result._isNegative = !result._isNegative;
679  return CPCalculationNoError;
680  }
681  if (CPDecimalIsZero(rightOperand))
682  {
683  _CPDecimalSet(result, leftOperand);
684  return CPCalculationNoError;
685  }
686 
687  var n1 = CPDecimalCopy(leftOperand),
688  n2 = CPDecimalCopy(rightOperand),
689  error1 = CPCalculationNoError;
690 
691  // For different signs use addition
692  if (leftOperand._isNegative != rightOperand._isNegative)
693  {
694  if (leftOperand._isNegative)
695  {
696  n1._isNegative = NO;
697  error1 = CPDecimalAdd(result, n1, rightOperand, roundingMode);
698  result._isNegative = YES;
699  if (error1 == CPCalculationUnderflow)
700  error1 = CPCalculationOverflow;
701  else if (error1 == CPCalculationOverflow) // gnustep has bug here
702  error1 = CPCalculationUnderflow;
703  return error1;
704  }
705  else
706  {
707  n2._isNegative = NO;
708  return CPDecimalAdd(result, leftOperand, n2, roundingMode);
709  }
710  }
711 
712  var error = CPDecimalNormalize(n1, n2, roundingMode),
713  comp = CPDecimalCompare(leftOperand, rightOperand);
714 
715  if (comp == CPOrderedSame)
716  {
717  _CPDecimalSetZero(result);
718  return CPCalculationNoError;
719  }
720 
721  // both negative, make positive and change order
722  if (leftOperand._isNegative)
723  {
724  n1._isNegative = NO;
725  n2._isNegative = NO;
726  if (comp == CPOrderedAscending)
727  {
728  error1 = _SimpleSubtract(result, n1, n2, roundingMode);
729  result._isNegative = YES;
730  }
731  else
732  {
733  error1 = _SimpleSubtract(result, n2, n1, roundingMode);
734  }
735  }
736  else
737  {
738  if (comp == CPOrderedAscending)
739  {
740  error1 = _SimpleSubtract(result, n2, n1, roundingMode);
741  result._isNegative = YES;
742  }
743  else
744  {
745  error1 = _SimpleSubtract(result, n1, n2, roundingMode);
746  }
747  }
748 
749  CPDecimalCompact(result);
750 
751  if (error1 == CPCalculationNoError)
752  return error;
753  else
754  return error1;
755 }
756 
757 // this is a very simple O(n^2) implementation that uses subtract. Are there faster divides?
758 function _SimpleDivide(result, leftOperand, rightOperand, roundingMode)
759 {
760  var error = CPCalculationNoError,
761  n1 = CPDecimalMakeZero(),
762  k = 0,
763  firsttime = YES,
764  stopk = CPDecimalMaxDigits + 1,
765  used = 0; // How many digits of l have been used?
766 
767  _CPDecimalSetZero(result);
768 
769  n1._mantissa = [];
770 
771  while ((used < leftOperand._mantissa.length) || (n1._mantissa.length
772  && !((n1._mantissa.length == 1) && (n1._mantissa[0] == 0))))
773  {
774  while (CPOrderedAscending == CPDecimalCompare(n1, rightOperand))
775  {
776  if (stopk == k)
777  break;
778  if (n1._exponent)
779  {
780  // Put back zeros removed by compacting
781  Array.prototype.push.call(n1._mantissa, 0);
782  n1._exponent--;
783  n1._isCompact = NO;
784  }
785  else
786  {
787  if (used < leftOperand._mantissa.length)
788  {
789  // Fill up with own digits
790  if (n1._mantissa.length || leftOperand._mantissa[used])
791  {
792  // only add 0 if there is already something
793  Array.prototype.push.call(n1._mantissa, (leftOperand._mantissa[used]));
794  n1._isCompact = NO;
795  }
796  used++;
797  }
798  else
799  {
800  if (result._exponent == CPDecimalMinExponent)
801  {
802  // use this as an end flag
803  k = stopk;
804  break;
805  }
806  // Borrow one digit
807  Array.prototype.push.call(n1._mantissa, 0);
808  result._exponent--;
809  }
810 
811  // Zeros must be added while enough digits are fetched to do the
812  // subtraction, but first time round this just add zeros at the
813  // start of the number , increases k, and hence reduces
814  // the available precision. To solve this only inc k/add zeros if
815  // this isn't first time round.
816  if (!firsttime)
817  {
818  k++;
819  result._mantissa[k - 1] = 0;
820  }
821  }
822  }
823 
824  // At this point digit in result we are working on is (k-1) so when
825  // k == (CPDecimalMaxDigits+1) then we should stop i.e. last subtract
826  // was last valid one.
827  if (stopk == k)
828  {
830  break;
831  }
832 
833  if (firsttime)
834  {
835  firsttime = NO;
836  k++;
837  }
838 
839  error1 = CPDecimalSubtract(n1, n1, rightOperand, roundingMode);
840  if (error1 != CPCalculationNoError)
841  error = error1;
842 
843  result._mantissa[k - 1]++;
844  }
845 
846  return error;
847 }
848 
858 function CPDecimalDivide(result, leftOperand, rightOperand, roundingMode)
859 {
860  var error = CPCalculationNoError,
861  exp = leftOperand._exponent - rightOperand._exponent,
862  neg = (leftOperand._isNegative != rightOperand._isNegative);
863 
864  if (leftOperand._isNaN || rightOperand._isNaN)
865  {
866  result._isNaN = YES;
867  return CPCalculationNoError;
868  }
869 
870  // check for zero
871  if (CPDecimalIsZero(rightOperand))
872  {
873  result._isNaN = YES;
875  }
876  if (CPDecimalIsZero(leftOperand))
877  {
878  _CPDecimalSetZero(result);
879  return CPCalculationNoError;
880  }
881 
882  //FIXME: Should also check for one
883 
884  var n1 = CPDecimalCopy(leftOperand),
885  n2 = CPDecimalCopy(rightOperand);
886 
887  n1._exponent = 0;
888  n1._isNegative = NO;
889  n2._exponent = 0;
890  n2._isNegative = NO;
891 
892  error = _SimpleDivide(result, n1, n2, roundingMode);
893  CPDecimalCompact(result);
894 
895  if (result._exponent + exp > CPDecimalMaxExponent)
896  {
897  result._isNaN = YES;
898  if (neg)
899  return CPCalculationUnderflow;
900  else
901  return CPCalculationOverflow;
902  }
903  else if (result._exponent + exp < CPDecimalMinExponent)
904  {
905  // We must cut off some digits
906  CPDecimalRound(result, result, exp + CPDecimalMaxExponent + 1, roundingMode);
908 
909  if (result._exponent + exp < CPDecimalMinExponent)
910  {
911  CPDecimalSetZero(result);
912  return error;
913  }
914  }
915  result._exponent += exp;
916  result._isNegative = neg;
917  return error;
918 }
919 
920 // Simple multiply O(n^2) , replace with something faster, like divide-n-conquer algo?
921 function _SimpleMultiply(result, leftOperand, rightOperand, roundingMode, powerMode)
922 {
923  var error = CPCalculationNoError,
924  carry = 0,
925  exp = 0,
926  n = CPDecimalMakeZero();
927 
928  _CPDecimalSetZero(result);
929 
930  // Do every digit of the second number
931  var i = 0;
932  for (; i < rightOperand._mantissa.length; i++)
933  {
934  _CPDecimalSetZero(n);
935 
936  n._exponent = rightOperand._mantissa.length - i - 1;
937  carry = 0;
938  d = rightOperand._mantissa[i];
939 
940  if (d == 0)
941  continue;
942 
943  var j = 0;
944  for (j = leftOperand._mantissa.length - 1; j >= 0; j--)
945  {
946  e = leftOperand._mantissa[j] * d + carry;
947  if (e >= 10)
948  {
949  carry = FLOOR(e / 10);
950  e = e % 10;
951  }
952  else
953  carry = 0;
954 
955  // This is one off to allow final carry
956  n._mantissa[j + 1] = e;
957  }
958  n._mantissa[0] = carry;
959 
960  CPDecimalCompact(n);
961 
962  error1 = CPDecimalAdd(result, result, n, roundingMode, YES);
963 
964  if (error1 != CPCalculationNoError)
965  error = error1;
966  }
967 
968  if (result._exponent + exp > CPDecimalMaxExponent)
969  {
970  // This should almost never happen
971  result._isNaN = YES;
972  return CPCalculationOverflow;
973  }
974  result._exponent += exp;
975 
976  // perform round to CPDecimalMaxDigits
977  if (result._mantissa.length > CPDecimalMaxDigits && !powerMode)
978  {
979  result._isCompact = NO;
980  var scale = CPDecimalMaxDigits - (result._mantissa.length + result._exponent);
981  CPDecimalRound(result, result, scale, roundingMode); // calls compact
982 
984  }
985  return error;
986 }
987 
997 function CPDecimalMultiply(result, leftOperand, rightOperand, roundingMode, powerMode)
998 {
999  var error = CPCalculationNoError,
1000  exp = leftOperand._exponent + rightOperand._exponent,
1001  neg = (leftOperand._isNegative != rightOperand._isNegative);
1002 
1003  if (leftOperand._isNaN || rightOperand._isNaN)
1004  {
1005  result._isNaN = YES;
1006  return CPCalculationNoError;
1007  }
1008 
1009  // check for zero
1010  if (CPDecimalIsZero(rightOperand) || CPDecimalIsZero(leftOperand))
1011  {
1012  _CPDecimalSetZero(result);
1013  return CPCalculationNoError;
1014  }
1015 
1016  //FIXME: Should also check for one
1017 
1018  if (exp > CPDecimalMaxExponent)
1019  {
1020  result._isNaN = YES;
1021  if (neg)
1022  return CPCalculationUnderflow;
1023  else
1024  return CPCalculationOverflow;
1025  }
1026 
1027  var n1 = CPDecimalCopy(leftOperand),
1028  n2 = CPDecimalCopy(rightOperand);
1029 
1030  n1._exponent = 0;
1031  n2._exponent = 0;
1032  n1._isNegative = NO;
1033  n2._isNegative = NO;
1034 
1035  // below is equiv. of simple compare
1036  var comp = 0,
1037  ll = n1._mantissa.length,
1038  lr = n2._mantissa.length;
1039  if (ll == lr)
1040  comp = CPOrderedSame;
1041  else if (ll > lr)
1042  comp = CPOrderedDescending;
1043  else
1044  comp = CPOrderedAscending;
1045 
1046 
1047  if (comp == CPOrderedDescending)
1048  {
1049  error = _SimpleMultiply(result, n1, n2, roundingMode, powerMode);
1050  }
1051  else
1052  {
1053  error = _SimpleMultiply(result, n2, n1, roundingMode, powerMode);
1054  }
1055 
1056  CPDecimalCompact(result);
1057 
1058  if (result._exponent + exp > CPDecimalMaxExponent)
1059  {
1060  result._isNaN = YES;
1061  if (neg)
1062  return CPCalculationUnderflow;
1063  else
1064  return CPCalculationOverflow;
1065  }
1066  else if (result._exponent + exp < CPDecimalMinExponent)
1067  {
1068  // We must cut off some digits
1069  CPDecimalRound(result, result, exp + CPDecimalMaxExponent + 1, roundingMode);
1071 
1072  if (result._exponent + exp < CPDecimalMinExponent)
1073  {
1074  _CPDecimalSetZero(result);
1075  return error;
1076  }
1077  }
1078 
1079  result._exponent += exp;
1080  result._isNegative = neg;
1081 
1082  return error;
1083 }
1084 
1094 function CPDecimalMultiplyByPowerOf10(result, dcm, power, roundingMode)
1095 {
1096  _CPDecimalSet(result, dcm);
1097  var p = result._exponent + power;
1098  if (p > CPDecimalMaxExponent)
1099  {
1100  result._isNaN = YES;
1101  return CPCalculationOverflow;
1102  }
1103  if (p < CPDecimalMinExponent)
1104  {
1105  result._isNaN = YES;
1106  return CPCalculationUnderflow;
1107  }
1108  result._exponent += power;
1109  return CPCalculationNoError;
1110 }
1111 
1121 function CPDecimalPower(result, dcm, power, roundingMode)
1122 {
1123  var error = CPCalculationNoError,
1124  neg = (dcm._isNegative && (power % 2)),
1125  n1 = CPDecimalCopy(dcm);
1126 
1127  n1._isNegative = NO;
1128 
1129  _CPDecimalSetOne(result);
1130 
1131  var e = power;
1132  while (e)
1133  {
1134  if (e & 1)
1135  {
1136  error = CPDecimalMultiply(result, result, n1, roundingMode); //, YES); // enable for high precision powers
1137  }
1138 
1139  error = CPDecimalMultiply(n1, n1, n1, roundingMode); //, YES); // enable for high precision powers
1140 
1141  e >>= 1;
1142 
1143  if (error > CPCalculationLossOfPrecision)
1144  break;
1145  }
1146 
1147  result._isNegative = neg;
1148 
1149 /* // enable is powerMode to do finally rounding to Max Digits.
1150  if ([result._mantissa count] > CPDecimalMaxDigits)
1151  {
1152  result._isCompact = NO;
1153  var scale = CPDecimalMaxDigits - ([result._mantissa count] + result._exponent);
1154  CPDecimalRound(result, result, scale ,roundingMode); // calls compact
1155  error = CPCalculationLossOfPrecision;
1156  }
1157 */
1158 
1159  CPDecimalCompact(result);
1160 
1161  return error;
1162 }
1163 
1173 function CPDecimalNormalize(dcm1, dcm2, roundingMode, longMode)
1174 {
1175  var factor = (longMode)?2:1;
1176 
1177  if (dcm1._isNaN || dcm2._isNaN)
1178  return CPCalculationNoError; // FIXME: correct behavior?
1179 
1180  // ensure compact
1181  if (!dcm1._isCompact)
1182  CPDecimalCompact(dcm1);
1183  if (!dcm2._isCompact)
1184  CPDecimalCompact(dcm2);
1185 
1186  if (dcm1._exponent == dcm2._exponent)
1187  return CPCalculationNoError;
1188 
1189  var e1 = dcm1._exponent,
1190  e2 = dcm2._exponent;
1191 
1192  // Add zeros
1193  var l2 = dcm2._mantissa.length,
1194  l1 = dcm1._mantissa.length,
1195  l = 0;
1196 
1197  var e = 0;
1198  if (e2 > e1 && e1 >= 0 && e2 >= 0)
1199  e = e2 - e1;
1200  else if (e2 > e1 && e1 < 0 && e2 >= 0)
1201  e = e2 - e1;
1202  else if (e2 > e1 && e1 < 0 && e2 < 0)
1203  e = e2 - e1;
1204  else if (e2 < e1 && e1 >= 0 && e2 >= 0)
1205  e = e1 - e2;
1206  else if (e2 < e1 && e1 >= 0 && e2 < 0)
1207  e = e1 - e2;
1208  else if (e2 < e1 && e1 < 0 && e2 < 0)
1209  e = e1 - e2;
1210 
1211  if (e2 > e1)
1212  l = MIN((CPDecimalMaxDigits * factor) - l2, e); //(e2 - e1));
1213  else
1214  l = MIN((CPDecimalMaxDigits * factor) - l1, e); //(e1 - e2));
1215 
1216  for (var i = 0; i < l; i++)
1217  {
1218  if (e2 > e1)
1219  Array.prototype.push.call(dcm2._mantissa, 0); //dcm2._mantissa[i + l2] = 0;
1220  else
1221  Array.prototype.push.call(dcm1._mantissa, 0);
1222  }
1223  if (e2 > e1)
1224  {
1225  dcm2._exponent -= l;
1226  dcm2._isCompact = NO;
1227  }
1228  else
1229  {
1230  dcm1._exponent -= l;
1231  dcm1._isCompact = NO;
1232  }
1233 
1234  // has been normalised?
1235  if (l != ABS(e2 - e1))//e2 - e1)
1236  {
1237  // no..
1238  // Round of some digits to increase exponent - will compact too
1239  // One number may become zero after this
1240  if (e2 > e1)
1241  {
1242  CPDecimalRound(dcm1, dcm1, -dcm2._exponent, roundingMode);
1243  l1 = CPDecimalIsZero(dcm1);
1244  }
1245  else
1246  {
1247  CPDecimalRound(dcm2, dcm2, -dcm1._exponent, roundingMode);
1248  l2 = CPDecimalIsZero(dcm2);
1249  }
1250 
1251  if ((dcm1._exponent != dcm2._exponent) && ((!l1) || (!l2)))
1252  {
1253  // Some zeros where cut of again by compacting
1254  if (e2 > e1)
1255  {
1256  l1 = dcm1._mantissa.length;
1257  l = MIN((CPDecimalMaxDigits * factor) - l1, ABS(dcm1._exponent - dcm2._exponent));
1258  for (var i = 0; i < l; i++)
1259  {
1260  dcm1._mantissa[i + l1] = 0; // or addObject: ? one faster than other?
1261  }
1262  dcm1._isCompact = NO;
1263  dcm1._exponent = dcm2._exponent;
1264  }
1265  else
1266  {
1267  l2 = dcm2._mantissa.length;
1268  l = MIN((CPDecimalMaxDigits * factor) - l2, ABS(dcm2._exponent - dcm1._exponent));
1269  for (var i = 0; i < l; i++)
1270  {
1271  dcm2._mantissa[i + l2] = 0; // or addObject: ? one faster than other?
1272  }
1273  dcm2._exponent = dcm1._exponent;
1274  dcm2._isCompact = NO;
1275  }
1276  }
1278  }
1279 
1280  return CPCalculationNoError;
1281 }
1282 
1295 function CPDecimalRound(result, dcm, scale ,roundingMode)
1296 {
1297  if (dcm._isNaN)
1298  return;
1299 
1300  if (!dcm._isCompact)
1301  CPDecimalCompact(dcm);
1302 
1303  // FIXME: check for valid inputs (eg scale etc)
1304 
1305  // FIXME: if in longMode should this double?
1306  if (scale == CPDecimalNoScale)
1307  return;
1308 
1309  _CPDecimalSet(result,dcm);
1310 
1311  var mc = result._mantissa.length,
1312  l = mc + scale + result._exponent;
1313 
1314  if (mc <= l)
1315  return;
1316  else if (l <= 0)
1317  {
1318  _CPDecimalSetZero(result);
1319  return;
1320  } else {
1321  var c = 0,
1322  n = 0,
1323  up = 0;
1324 
1325  // Adjust length and exponent
1326  result._exponent += mc - l;
1327 
1328  switch (roundingMode)
1329  {
1330  case CPRoundDown:
1331  up = result._isNegative;
1332  break;
1333  case CPRoundUp:
1334  up = !result._isNegative;
1335  break;
1336  case CPRoundPlain:
1337  n = result._mantissa[l];
1338  up = (n >= 5);
1339  break;
1340  case _CPRoundHalfDown:
1341  n = result._mantissa[l];
1342  up = (n > 5);
1343  break;
1344  case CPRoundBankers:
1345  n = result._mantissa[l];
1346  if (n > 5)
1347  up = YES;
1348  else if (n < 5)
1349  up = NO;
1350  else
1351  {
1352  if (l == 0)
1353  c = 0;
1354  else
1355  c = result._mantissa[l - 1];
1356  up = ((c % 2) != 0);
1357  }
1358  break;
1359  default:
1360  up = NO;
1361  break;
1362  }
1363  // cut mantissa
1364  result._mantissa = Array.prototype.slice.call(result._mantissa, 0, l);
1365 
1366  if (up)
1367  {
1368  for (var i = l-1; i >= 0; i--)
1369  {
1370  if (result._mantissa[i] != 9)
1371  {
1372  result._mantissa[i]++;
1373  break;
1374  }
1375  result._mantissa[i] = 0;
1376  }
1377  // Final overflow?
1378  if (i == -1)
1379  {
1380  // As all digits are zeros, just change the first
1381  result._mantissa[0] = 1;
1382  if (result._exponent >= CPDecimalMaxExponent)
1383  {
1384  // Overflow in rounding.
1385  // Add one zero add the end. There must be space as
1386  // we just cut off some digits.
1387  Array.prototype.push.call(result._mantissa, 0);
1388  }
1389  else
1390  result._exponent++;
1391  }
1392  }
1393  }
1394 
1395  CPDecimalCompact(result);
1396 }
1397 
1403 function CPDecimalCompact(dcm)
1404 {
1405  // if positive or zero exp leading zeros simply delete, trailing ones u need to increment exponent
1406  if (!dcm || dcm._mantissa.length == 0 || CPDecimalIsNotANumber(dcm))
1407  return;
1408 
1409  if (CPDecimalIsZero(dcm))
1410  {
1411  // handle zero number compacting
1412  _CPDecimalSetZero(dcm);
1413  return;
1414  }
1415  // leading zeros, when exponent is zero these mean we need to move our decimal point to compact
1416  // if exp is zero does it make sense to have them? don't think so so delete them
1417  while (dcm._mantissa[0] === 0)
1418  {
1419  Array.prototype.shift.call(dcm._mantissa);
1420  }
1421  // trailing zeros, strip them
1422  while (dcm._mantissa[dcm._mantissa.length - 1] === 0)
1423  {
1424  Array.prototype.pop.call(dcm._mantissa);
1425  dcm._exponent++;
1426  if (dcm._exponent + 1 > CPDecimalMaxExponent)
1427  {
1428  // TODO: test case for this
1429  // overflow if we compact anymore, so don't
1430  break;
1431  }
1432  }
1433 
1434  dcm._isCompact = YES;
1435 }
1436 
1444 function CPDecimalString(dcm, locale)
1445 {
1446  // Cocoa seems to just add all the zeros... this maybe controlled by locale,
1447  // will check.
1448  if (dcm._isNaN)
1449  return @"NaN";
1450 
1451  var string = @"",
1452  i = 0;
1453 
1454  if (dcm._isNegative)
1455  string += "-";
1456 
1457  var k = dcm._mantissa.length,
1458  l = ((dcm._exponent < 0) ? dcm._exponent : 0) + k;
1459 
1460  if (l < 0)
1461  {
1462  // add leading zeros
1463  string += "0.";
1464  for (i = 0; i < ABS(l); i++)
1465  {
1466  string += "0";
1467  }
1468  l = k;
1469  }
1470  else if (l == 0)
1471  {
1472  string += "0";
1473  }
1474 
1475  for (i = 0; i < l; i++)
1476  {
1477  string += dcm._mantissa[i];
1478  }
1479  if (l < k)
1480  {
1481  string += ".";
1482  for (i = l; i < k; i++)
1483  {
1484  string += dcm._mantissa[i];
1485  }
1486  }
1487  for (i = 0; i < dcm._exponent; i++)
1488  {
1489  string += "0";
1490  }
1491 
1492  return string;
1493  /*
1494  // GNUStep
1495  if (dcm._isNaN)
1496  return @"NaN";
1497 
1498  var sep = 0;
1499  if ((locale == nil) || (sep = [locale objectForKey: CPDecimalSeparator]) == nil)
1500  sep = @".";
1501 
1502  if (CPDecimalIsZero(dcm))
1503  return @"0" + sep + "0";
1504 
1505  var string = @"";
1506 
1507  if (dcm._isNegative)
1508  string += "-";
1509 
1510  var len = [dcm._mantissa count],
1511  size = len + dcm._exponent;
1512 
1513  if ((len <= 6) && (0 < size) && (size < 7))
1514  {
1515  // For small numbers use the normal format
1516  var i = 0
1517  for (; i < len; i++)
1518  {
1519  if (size == i)
1520  string += sep;
1521  d = dcm._mantissa[i];
1522  string += d.toString();
1523  }
1524  for (i = 0; i < dcm._exponent; i++)
1525  {
1526  string += "0";
1527  }
1528  }
1529  else if ((len <= 6) && (0 >= size) && (size > -3))
1530  {
1531  // For small numbers use the normal format
1532  string += "0";
1533  string += sep;
1534 
1535  var i = 0;
1536  for (; i > size; i--)
1537  {
1538  string += "0";
1539  }
1540  for (i = 0; i < len; i++)
1541  {
1542  d = dcm._mantissa[i];
1543  string += d.toString();
1544  }
1545  }
1546  else
1547  {
1548  // Scientific format
1549  var i = 0;
1550  for (; i < len; i++)
1551  {
1552  if (1 == i)
1553  string += sep;
1554  d = dcm._mantissa[i];
1555  string += d.toString();
1556  }
1557  if (size != 1)
1558  {
1559  //s = [NSString stringWithFormat: @"E%d", size-1];
1560  //[string appendString: s];
1561  string += "E" + (size - 1).toString();
1562  }
1563  }
1564 
1565  return string;
1566  */
1567 }