API 0.9.5
Foundation/CPDecimal.j
Go to the documentation of this file.
00001 /*
00002  * CPDecimal.j
00003  * Foundation
00004  *
00005  * Created by Stephen Paul Ierodiaconou
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this library; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 
00022  /*
00023    Ported From GNUStep :
00024 
00025    NSDecimal functions
00026    Copyright (C) 2000 Free Software Foundation, Inc.
00027 
00028    Written by: Fred Kiefer <FredKiefer@gmx.de>
00029    Created: July 2000
00030 
00031    This file is part of the GNUstep Base Library.
00032 
00033    This library is free software; you can redistribute it and/or
00034    modify it under the terms of the GNU Lesser General Public
00035    License as published by the Free Software Foundation; either
00036    version 2 of the License, or (at your option) any later version.
00037 
00038    This library is distributed in the hope that it will be useful,
00039    but WITHOUT ANY WARRANTY; without even the implied warranty of
00040    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00041    Library General Public License for more details.
00042 
00043    You should have received a copy of the GNU Lesser General Public
00044    License along with this library; if not, write to the Free
00045    Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00046    Boston, MA 02111 USA.
00047 
00048    <title>NSDecimal class reference</title>
00049    $Date: 2008-06-12 04:44:00 -0600 (Thu, 12 Jun 2008) $ $Revision: 26630 $
00050    */
00051 
00052 
00053 // Decimal size limits
00054 CPDecimalMaxDigits                   =   38;
00055 CPDecimalMaxExponent                 =  127;
00056 CPDecimalMinExponent                 = -128;
00057 
00058 // Scale for no Rounding
00059 CPDecimalNoScale                     = 128
00060 
00061 // CPCalculationError enum
00062 CPCalculationNoError                 = 0;
00063 CPCalculationLossOfPrecision         = 1;
00064 CPCalculationOverflow                = 2;
00065 CPCalculationUnderflow               = 3;
00066 CPCalculationDivideByZero            = 4;
00067 
00068 //CPRoundingMode Enum
00069 CPRoundPlain                         = 1;
00070 CPRoundDown                          = 2;
00071 CPRoundUp                            = 3;
00072 CPRoundBankers                       = 4;
00073 _CPRoundHalfDown                     = 5; // Private API rounding mode used by CPNumberFormatter.
00074 
00075 //Exceptions
00076 CPDecimalNumberOverflowException     = @"CPDecimalNumberOverflowException";
00077 CPDecimalNumberUnderflowException    = @"CPDecimalNumberUnderflowException";
00078 CPDecimalNumberExactnessException    = @"CPDecimalNumberExactnessException";
00079 CPDecimalNumberDivideByZeroException = @"CPDecimalNumberDivideByZeroException";
00080 
00081 /*
00082 Initialisers for NSDecimal do not exist so here I have created my own.
00083 The coefficient is called the 'mantissa' in this implementation as this is what Cocoa calls it.
00084 
00085 CPDecimal format:
00086     ._mantissa   : CPArray, containing each digit of the number as an unsigned integer.
00087     ._exponent   : integer, the exponent of the number as an signed integer.
00088     ._isNegative : BOOL, sign of number
00089     ._isCompact  : BOOL, has number been compacted.
00090     ._isNaN      : BOOL, is NaN (Not a number) i.e. number is invalid.
00091 */
00092 
00100 // FIXME: locale support and Cocoaify, needs to accept .1 and leading 0s
00101 function CPDecimalMakeWithString(string, locale)
00102 {
00103     if (!string)
00104         return CPDecimalMakeNaN();
00105 
00106     // Regexp solution as found in JSON spec, with working regexp (I added groupings)
00107     // Test here: http://www.regexplanet.com/simple/index.html
00108     // Info from: http://stackoverflow.com/questions/638565/parsing-scientific-notation-sensibly
00109     // ([+\-]?)((?:0|[1-9]\d*))  - integer part, cant have leading zeros
00110     //  (?:\.(\d*))?           - optional decimal part plus number in group
00111     //  (?:[eE]([+\-]?)(\d+))?   - optional exponent part plus number in group
00112     // group 0: string, 1: sign, 2: integer, 3: decimal, 4: exponent sign, 5: exponent
00113 
00114     // Note: this doesn't accept .01 for example, should it?
00115     // If yes simply add '?' after integer part group, i.e. ([+\-]?)((?:0|[1-9]\d*)?)
00116     var matches = string.match(/^([+\-]?)((?:0|[1-9]\d*))(?:\.(\d*))?(?:[eE]([+\-]?)(\d+))?$/);
00117     if (!matches)
00118         return CPDecimalMakeNaN();
00119 
00120     var ds = matches[1],
00121         intpart = matches[2],
00122         decpart = matches[3],
00123         es = matches[4],
00124         exp = matches[5];
00125 
00126     var isNegative = NO;
00127     if (ds && ds === "-")
00128         isNegative = YES;
00129 
00130     var exponent = 0;
00131     if (exp)
00132         exponent = parseInt(exp) * ((es && es === "-")?-1:1);
00133 
00134     if (decpart) // push decimal point to last digit, then let compact handle the zeros
00135         exponent -= decpart.length;
00136 
00137     var inputlength = (intpart?intpart.length:0) + (decpart?decpart.length:0);
00138     if (inputlength > CPDecimalMaxDigits)
00139     {
00140         // input is too long, increase exponent and truncate
00141         exponent += inputlength - CPDecimalMaxDigits;
00142     }
00143 
00144     if (exponent > CPDecimalMaxExponent || exponent < CPDecimalMinExponent)
00145         return CPDecimalMakeNaN();
00146 
00147     // Representation internally starts at most significant digit
00148     var m = [],
00149         i = 0;
00150     for (; i < (intpart?intpart.length:0); i++)
00151     {
00152         if (i >= CPDecimalMaxDigits)
00153             break; // truncate
00154         Array.prototype.push.call(m, parseInt(intpart.charAt(i)));
00155     }
00156     var j = 0;
00157     for (; j < (decpart?decpart.length:0); j++)
00158     {
00159         if ((i + j) >= CPDecimalMaxDigits)
00160             break; // truncate
00161         Array.prototype.push.call(m, parseInt(decpart.charAt(j)));
00162     }
00163 
00164     var dcm = {_exponent:exponent, _isNegative:isNegative, _isCompact:NO, _isNaN:NO, _mantissa:m};
00165     CPDecimalCompact(dcm);
00166 
00167     return dcm;
00168 }
00169 
00177 function CPDecimalMakeWithParts(mantissa, exponent)
00178 {
00179     var m = [],
00180         isNegative = NO;
00181 
00182     if (mantissa < 0 )
00183     {
00184         isNegative = YES;
00185         mantissa = ABS(mantissa);
00186     }
00187 
00188     if (mantissa == 0)
00189         Array.prototype.push.call(m, 0);
00190 
00191     if (exponent > CPDecimalMaxExponent || exponent < CPDecimalMinExponent)
00192         return CPDecimalMakeNaN();
00193 
00194     // remaining digits are disposed of via truncation
00195     while ((mantissa > 0) && (m.length < CPDecimalMaxDigits)) // count selector here could be optimised away
00196     {
00197         Array.prototype.unshift.call(m, parseInt(mantissa % 10));
00198         mantissa = FLOOR(mantissa / 10);
00199     }
00200 
00201     var dcm = {_exponent:exponent, _isNegative:isNegative, _isCompact:YES, _isNaN:NO, _mantissa:m};
00202 
00203     CPDecimalCompact(dcm);
00204 
00205     return dcm;
00206 }
00207 
00213 function CPDecimalMakeZero()
00214 {
00215     return CPDecimalMakeWithParts(0, 0);
00216 }
00217 
00223 function CPDecimalMakeOne()
00224 {
00225     return CPDecimalMakeWithParts(1, 0);
00226 }
00227 
00228 
00234 function CPDecimalMakeNaN()
00235 {
00236     var d = CPDecimalMakeWithParts(0, 0);
00237     d._isNaN = YES;
00238     return d;
00239 }
00240 
00241 // private methods
00242 function _CPDecimalMakeMaximum()
00243 {
00244     var s = @"",
00245         i = 0;
00246     for (; i < CPDecimalMaxDigits; i++)
00247         s += "9";
00248     s += "e" + CPDecimalMaxExponent;
00249     return CPDecimalMakeWithString(s);
00250 }
00251 
00252 function _CPDecimalMakeMinimum()
00253 {
00254     var s = @"-",
00255         i = 0;
00256     for (; i < CPDecimalMaxDigits; i++)
00257         s += "9";
00258     s += "e" + CPDecimalMaxExponent;
00259     return CPDecimalMakeWithString(s);
00260 }
00261 
00267 function CPDecimalIsZero(dcm)
00268 {
00269     // exponent doesn't matter as long as mantissa = 0
00270     if (!dcm._isNaN)
00271     {
00272         for (var i = 0; i < dcm._mantissa.length; i++)
00273             if (dcm._mantissa[i] !== 0)
00274                 return NO;
00275 
00276         return YES;
00277     }
00278 
00279     return NO;
00280 }
00281 
00287 function CPDecimalIsOne(dcm)
00288 {
00289     CPDecimalCompact(dcm);
00290     // exponent doesn't matter as long as mantissa = 0
00291     if (!dcm._isNaN)
00292     {
00293         if (dcm._mantissa && (dcm._mantissa.length == 1) && (dcm._mantissa[0] == 1))
00294             return YES;
00295     }
00296     return NO;
00297 }
00298 
00299 //private method to copy attribute values
00300 function _CPDecimalSet(t,s)
00301 {
00302     // should all be [x copy] ?
00303     t._exponent = s._exponent;
00304     t._isNegative = s._isNegative;
00305     t._isCompact = s._isCompact;
00306     t._isNaN = s._isNaN;
00307     t._mantissa = Array.prototype.slice.call(s._mantissa, 0);
00308 }
00309 
00310 function _CPDecimalSetZero(result)
00311 {
00312     result._mantissa = [0];
00313     result._exponent = 0;
00314     result._isNegative = NO;
00315     result._isCompact = YES;
00316     result._isNaN = NO;
00317 }
00318 
00319 function _CPDecimalSetOne(result)
00320 {
00321     result._mantissa = [1];
00322     result._exponent = 0;
00323     result._isNegative = NO;
00324     result._isCompact = YES;
00325     result._isNaN = NO;
00326 }
00327 
00333 function CPDecimalIsNotANumber(dcm)
00334 {
00335     return (dcm._isNaN)?YES:NO;
00336 }
00337 
00344 function CPDecimalCopy(dcm)
00345 {
00346     return {_exponent:dcm._exponent,
00347             _isNegative:dcm._isNegative,
00348             _isCompact:dcm._isCompact,
00349             _isNaN:dcm._isNaN,
00350             _mantissa:Array.prototype.slice.call(dcm._mantissa, 0)
00351             };
00352 }
00353 
00362 function CPDecimalCompare(leftOperand, rightOperand)
00363 {
00364     if (leftOperand._isNaN && rightOperand._isNaN)
00365         return CPOrderedSame;
00366 
00367     if (leftOperand._isNegative != rightOperand._isNegative)
00368     {
00369         if (rightOperand._isNegative)
00370             return CPOrderedDescending;
00371         else
00372             return CPOrderedAscending;
00373     }
00374 
00375     // Before comparing number size check if zero (dont use CPDecimalIsZero as it is more computationally intensive)
00376     var leftIsZero = (leftOperand._mantissa.length == 1 && leftOperand._mantissa[0] == 0),
00377         rightIsZero = (rightOperand._mantissa.length == 1 && rightOperand._mantissa[0] == 0),
00378         // Sign is the same, quick check size (length + exp)
00379         s1 = leftOperand._exponent + leftOperand._mantissa.length,
00380         s2 = rightOperand._exponent + rightOperand._mantissa.length;
00381 
00382     if (leftIsZero || s1 < s2)
00383     {
00384         if (rightOperand._isNegative)
00385             return CPOrderedDescending;
00386         else
00387             return CPOrderedAscending;
00388     }
00389     if (rightIsZero || s1 > s2)
00390     {
00391         if (rightOperand._isNegative)
00392             return CPOrderedAscending;
00393         else
00394             return CPOrderedDescending;
00395     }
00396 
00397     // Same size, so check mantissa
00398     var l = MIN(leftOperand._mantissa.length, rightOperand._mantissa.length),
00399         i = 0;
00400 
00401     for (; i < l; i++)
00402     {
00403         var d = rightOperand._mantissa[i] - leftOperand._mantissa[i];
00404 
00405         if (d > 0)
00406         {
00407             if (rightOperand._isNegative)
00408                 return CPOrderedDescending;
00409             else
00410                 return CPOrderedAscending;
00411         }
00412         if (d < 0)
00413         {
00414             if (rightOperand._isNegative)
00415                 return CPOrderedAscending;
00416             else
00417                 return CPOrderedDescending;
00418         }
00419     }
00420 
00421     // Same digits, check length
00422     if (leftOperand._mantissa.length > rightOperand._mantissa.length)
00423     {
00424         if (rightOperand._isNegative)
00425             return CPOrderedAscending;
00426         else
00427             return CPOrderedDescending;
00428     }
00429     if (leftOperand._mantissa.length < rightOperand._mantissa.length)
00430     {
00431         if (rightOperand._isNegative)
00432             return CPOrderedDescending;
00433         else
00434             return CPOrderedAscending;
00435     }
00436 
00437     return CPOrderedSame;
00438 }
00439 
00440 // GNUSteps addition. This is standard O(n) complexity.
00441 // longMode makes the addition not round for up to double max digits, this is
00442 // to preserve precision during multiplication
00443 function _SimpleAdd(result, leftOperand, rightOperand, roundingMode, longMode)
00444 {
00445     var factor = (longMode)?2:1;
00446 
00447     _CPDecimalSet(result, leftOperand);
00448 
00449     var j = leftOperand._mantissa.length - rightOperand._mantissa.length,
00450         l = rightOperand._mantissa.length,
00451         i = l - 1,
00452         carry = 0,
00453         error = CPCalculationNoError;
00454 
00455     // Add all the digits
00456     for (; i >= 0; i--)
00457     {
00458         var d = rightOperand._mantissa[i] + result._mantissa[i + j] + carry;
00459         if (d >= 10)
00460         {
00461             d = d % 10;  // a division. subtraction and conditions faster?
00462             carry = 1;
00463         }
00464         else
00465             carry = 0;
00466 
00467         result._mantissa[i + j] = d;
00468     }
00469 
00470     if (carry)
00471     {
00472         for (i = j - 1; i >= 0; i--)
00473         {
00474             if (result._mantissa[i] != 9)
00475             {
00476                 result._mantissa[i]++;
00477                 carry = 0;
00478                 break;
00479             }
00480             result._mantissa[i] = 0;
00481         }
00482 
00483         if (carry)
00484         {
00485             Array.prototype.splice.call(result._mantissa, 0, 0, 1);
00486 
00487             // The number must be shifted to the right
00488             if ((CPDecimalMaxDigits * factor) == leftOperand._mantissa.length)
00489             {
00490                 var scale = - result._exponent - 1;
00491                 CPDecimalRound(result, result, scale, roundingMode);
00492             }
00493 
00494             if (CPDecimalMaxExponent < result._exponent)
00495             {
00496                 result._isNaN = YES;
00497                 error = CPCalculationOverflow;
00498                 result._exponent = CPDecimalMaxExponent;
00499             }
00500         }
00501     }
00502     return error;
00503 }
00504 
00514 function CPDecimalAdd(result, leftOperand, rightOperand, roundingMode, longMode)
00515 {
00516     if (leftOperand._isNaN || rightOperand._isNaN)
00517     {
00518         result._isNaN = YES;
00519         return CPCalculationNoError;
00520     }
00521     // check for zero
00522     if (CPDecimalIsZero(leftOperand))
00523     {
00524         _CPDecimalSet(result, rightOperand);
00525         return CPCalculationNoError;
00526     }
00527     if (CPDecimalIsZero(rightOperand))
00528     {
00529         _CPDecimalSet(result, leftOperand);
00530         return CPCalculationNoError;
00531     }
00532 
00533      var n1 = CPDecimalCopy(leftOperand),
00534          n2 = CPDecimalCopy(rightOperand);
00535 
00536     // For different signs use subtraction
00537     if (leftOperand._isNegative != rightOperand._isNegative)
00538     {
00539         if (leftOperand._isNegative)
00540         {
00541             n1._isNegative = NO;
00542             return CPDecimalSubtract(result, rightOperand, n1, roundingMode);
00543         }
00544         else
00545         {
00546             n2._isNegative = NO;
00547             return CPDecimalSubtract(result, leftOperand, n2, roundingMode);
00548         }
00549     }
00550 
00551     var normerror = CPDecimalNormalize(n1, n2, roundingMode, longMode);
00552 
00553     // below is equiv. of simple compare
00554     var comp = 0,
00555         ll = n1._mantissa.length,
00556         lr = n2._mantissa.length;
00557     if (ll == lr)
00558         comp = CPOrderedSame;
00559     else if (ll > lr)
00560         comp = CPOrderedDescending;
00561     else
00562         comp = CPOrderedAscending;
00563 
00564     // both negative, make positive
00565     if (leftOperand._isNegative)
00566     {
00567         n1._isNegative = NO;
00568         n2._isNegative = NO;
00569         // SimpleCompare does not look at sign
00570         if (comp == CPOrderedDescending)
00571         {
00572             adderror = _SimpleAdd(result, n1, n2, roundingMode, longMode);
00573         }
00574         else
00575         {
00576             adderror = _SimpleAdd(result, n2, n1, roundingMode, longMode);
00577         }
00578         result._isNegative = YES;
00579         // swap sign over over/underflow exception
00580         if (CPCalculationUnderflow == adderror)
00581             adderror = CPCalculationOverflow;
00582         else if (CPCalculationUnderflow == adderror)
00583             adderror = CPCalculationUnderflow;
00584     }
00585     else
00586     {
00587         if (comp == CPOrderedAscending)
00588         {
00589             adderror = _SimpleAdd(result, n2, n1, roundingMode, longMode);
00590         }
00591         else
00592         {
00593             adderror = _SimpleAdd(result, n1, n2, roundingMode, longMode);
00594         }
00595     }
00596 
00597     CPDecimalCompact(result);
00598 
00599     if (adderror == CPCalculationNoError)
00600         return normerror;
00601     else
00602         return adderror;
00603 }
00604 
00605 // GNUStep port internal subtract
00606 function _SimpleSubtract(result, leftOperand, rightOperand, roundingMode)
00607 {
00608     var error = CPCalculationNoError,
00609         borrow = 0,
00610         l = rightOperand._mantissa.length,
00611         j = leftOperand._mantissa.length - l,
00612         i = l - 1;
00613 
00614     _CPDecimalSet(result, leftOperand);
00615 
00616     // Now subtract all digits
00617     for (; i >= 0; i--)
00618     {
00619         var d = result._mantissa[i + j] - rightOperand._mantissa[i] - borrow;
00620         if (d < 0)
00621         {
00622             d = d + 10;
00623             borrow = 1;
00624         }
00625         else
00626             borrow = 0;
00627 
00628         result._mantissa[i + j] = d;
00629     }
00630 
00631     if (borrow)
00632     {
00633         for (i = j - 1; i >= 0; i--)
00634         {
00635             if (result._mantissa[i] != 0)
00636             {
00637                 result._mantissa[i]--;
00638                 break;
00639             }
00640             result._mantissa[i] = 9;
00641         }
00642 
00643         if (-1 == i)
00644         {
00645             error = nil;
00646         }
00647     }
00648 
00649     return error;
00650 }
00651 
00661 function CPDecimalSubtract(result, leftOperand, rightOperand, roundingMode)
00662 {
00663     if (leftOperand._isNaN || rightOperand._isNaN)
00664     {
00665         result._isNaN = YES;
00666         return CPCalculationNoError;
00667     }
00668 
00669     // check for zero
00670     if (CPDecimalIsZero(leftOperand))
00671     {
00672         _CPDecimalSet(result, rightOperand);
00673         result._isNegative = !result._isNegative;
00674         return CPCalculationNoError;
00675     }
00676     if (CPDecimalIsZero(rightOperand))
00677     {
00678         _CPDecimalSet(result, leftOperand);
00679         return CPCalculationNoError;
00680     }
00681 
00682      var n1 = CPDecimalCopy(leftOperand),
00683          n2 = CPDecimalCopy(rightOperand),
00684          error1 = CPCalculationNoError;
00685 
00686     // For different signs use addition
00687     if (leftOperand._isNegative != rightOperand._isNegative)
00688     {
00689         if (leftOperand._isNegative)
00690         {
00691             n1._isNegative = NO;
00692             error1 = CPDecimalAdd(result, n1, rightOperand, roundingMode);
00693             result._isNegative = YES;
00694             if (error1 == CPCalculationUnderflow)
00695                 error1 = CPCalculationOverflow;
00696             else if (error1 == CPCalculationOverflow) // gnustep has bug here
00697                 error1 = CPCalculationUnderflow;
00698             return error1;
00699         }
00700         else
00701         {
00702             n2._isNegative = NO;
00703             return CPDecimalAdd(result, leftOperand, n2, roundingMode);
00704         }
00705     }
00706 
00707     var error = CPDecimalNormalize(n1, n2, roundingMode),
00708         comp = CPDecimalCompare(leftOperand, rightOperand);
00709 
00710     if (comp == CPOrderedSame)
00711     {
00712         _CPDecimalSetZero(result);
00713         return CPCalculationNoError;
00714     }
00715 
00716     // both negative, make positive and change order
00717     if (leftOperand._isNegative)
00718     {
00719         n1._isNegative = NO;
00720         n2._isNegative = NO;
00721         if (comp == CPOrderedAscending)
00722         {
00723             error1 = _SimpleSubtract(result, n1, n2, roundingMode);
00724             result._isNegative = YES;
00725         }
00726         else
00727         {
00728             error1 = _SimpleSubtract(result, n2, n1, roundingMode);
00729         }
00730     }
00731     else
00732     {
00733         if (comp == CPOrderedAscending)
00734         {
00735             error1 = _SimpleSubtract(result, n2, n1, roundingMode);
00736             result._isNegative = YES;
00737         }
00738         else
00739         {
00740             error1 = _SimpleSubtract(result, n1, n2, roundingMode);
00741         }
00742     }
00743 
00744     CPDecimalCompact(result);
00745 
00746     if (error1 == CPCalculationNoError)
00747         return error;
00748     else
00749         return error1;
00750 }
00751 
00752 // this is a very simple O(n^2) implementation that uses subtract. Are there faster divides?
00753 function _SimpleDivide(result, leftOperand, rightOperand, roundingMode)
00754 {
00755     var error = CPCalculationNoError,
00756         n1 = CPDecimalMakeZero(),
00757         k = 0,
00758         firsttime = YES,
00759         stopk = CPDecimalMaxDigits + 1,
00760         used = 0; // How many digits of l have been used?
00761 
00762     _CPDecimalSetZero(result);
00763 
00764     n1._mantissa = [];
00765 
00766     while ((k < leftOperand._mantissa.length) || (n1._mantissa.length
00767                                                     && !((n1._mantissa.length == 1) && (n1._mantissa[0] == 0))))
00768     {
00769         while (CPOrderedAscending == CPDecimalCompare(n1, rightOperand))
00770         {
00771             if (stopk == k)
00772                 break;
00773             if (n1._exponent)
00774             {
00775                 // Put back zeros removed by compacting
00776                 Array.prototype.push.call(n1._mantissa, 0);
00777                 n1._exponent--;
00778                 n1._isCompact = NO;
00779             }
00780             else
00781             {
00782                 if (used < leftOperand._mantissa.length)
00783                 {
00784                     // Fill up with own digits
00785                     if (n1._mantissa.length || leftOperand._mantissa[used])
00786                     {
00787                         // only add 0 if there is already something
00788                         Array.prototype.push.call(n1._mantissa, (leftOperand._mantissa[used]));
00789                         n1._isCompact = NO;
00790                     }
00791                     used++;
00792                 }
00793                 else
00794                 {
00795                     if (result._exponent == CPDecimalMinExponent)
00796                     {
00797                         // use this as an end flag
00798                         k = stopk;
00799                         break;
00800                     }
00801                     // Borrow one digit
00802                     Array.prototype.push.call(n1._mantissa, 0);
00803                     result._exponent--;
00804                 }
00805 
00806                 // Zeros must be added while enough digits are fetched to do the
00807                 // subtraction, but first time round this just add zeros at the
00808                 // start of the number , increases k, and hence reduces
00809                 // the available precision. To solve this only inc k/add zeros if
00810                 // this isn't first time round.
00811                 if (!firsttime)
00812                 {
00813                     k++;
00814                     result._mantissa[k - 1] = 0;
00815                 }
00816             }
00817         }
00818 
00819         // At this point digit in result we are working on is (k-1) so when
00820         // k == (CPDecimalMaxDigits+1) then we should stop i.e. last subtract
00821         // was last valid one.
00822         if (stopk == k)
00823         {
00824             error = CPCalculationLossOfPrecision;
00825             break;
00826         }
00827 
00828         if (firsttime)
00829         {
00830             firsttime = NO;
00831             k++;
00832         }
00833 
00834         error1 = CPDecimalSubtract(n1, n1, rightOperand, roundingMode);
00835         if (error1 != CPCalculationNoError)
00836             error = error1;
00837 
00838         result._mantissa[k - 1]++;
00839     }
00840 
00841     return error;
00842 }
00843 
00853 function CPDecimalDivide(result, leftOperand, rightOperand, roundingMode)
00854 {
00855     var error = CPCalculationNoError,
00856         exp = leftOperand._exponent - rightOperand._exponent,
00857         neg = (leftOperand._isNegative != rightOperand._isNegative);
00858 
00859     if (leftOperand._isNaN || rightOperand._isNaN)
00860     {
00861         result._isNaN = YES;
00862         return CPCalculationNoError;
00863     }
00864 
00865     // check for zero
00866     if (CPDecimalIsZero(rightOperand))
00867     {
00868         result._isNaN = YES;
00869         return CPCalculationDivideByZero;
00870     }
00871     if (CPDecimalIsZero(leftOperand))
00872     {
00873         _CPDecimalSetZero(result);
00874         return CPCalculationNoError;
00875     }
00876 
00877     //FIXME: Should also check for one
00878 
00879     var n1 = CPDecimalCopy(leftOperand),
00880         n2 = CPDecimalCopy(rightOperand);
00881 
00882     n1._exponent = 0;
00883     n1._isNegative = NO;
00884     n2._exponent = 0;
00885     n2._isNegative = NO;
00886 
00887     error = _SimpleDivide(result, n1, n2, roundingMode);
00888     CPDecimalCompact(result);
00889 
00890     if (result._exponent + exp > CPDecimalMaxExponent)
00891     {
00892         result._isNaN = YES;
00893         if (neg)
00894             return CPCalculationUnderflow;
00895         else
00896             return CPCalculationOverflow;
00897     }
00898     else if (result._exponent + exp < CPDecimalMinExponent)
00899     {
00900         // We must cut off some digits
00901         CPDecimalRound(result, result, exp + CPDecimalMaxExponent + 1, roundingMode);
00902         error = CPCalculationLossOfPrecision;
00903 
00904         if (result._exponent + exp < CPDecimalMinExponent)
00905         {
00906             CPDecimalSetZero(result);
00907             return error;
00908         }
00909     }
00910     result._exponent += exp;
00911     result._isNegative = neg;
00912     return error;
00913 }
00914 
00915 // Simple multiply O(n^2) , replace with something faster, like divide-n-conquer algo?
00916 function _SimpleMultiply(result, leftOperand, rightOperand, roundingMode, powerMode)
00917 {
00918     var error = CPCalculationNoError,
00919         carry = 0,
00920         exp = 0,
00921         n = CPDecimalMakeZero();
00922 
00923     _CPDecimalSetZero(result);
00924 
00925     // Do every digit of the second number
00926     var i = 0;
00927     for (; i < rightOperand._mantissa.length; i++)
00928     {
00929         _CPDecimalSetZero(n);
00930 
00931         n._exponent = rightOperand._mantissa.length - i - 1;
00932         carry = 0;
00933         d = rightOperand._mantissa[i];
00934 
00935         if (d == 0)
00936             continue;
00937 
00938         var j = 0;
00939         for (j = leftOperand._mantissa.length - 1; j >= 0; j--)
00940         {
00941             e = leftOperand._mantissa[j] * d + carry;
00942             if (e >= 10)
00943             {
00944                 carry = FLOOR(e / 10);
00945                 e = e % 10;
00946             }
00947             else
00948                 carry = 0;
00949 
00950             // This is one off to allow final carry
00951             n._mantissa[j + 1] = e;
00952         }
00953         n._mantissa[0] = carry;
00954 
00955         CPDecimalCompact(n);
00956 
00957         error1 = CPDecimalAdd(result, result, n, roundingMode, YES);
00958 
00959         if (error1 != CPCalculationNoError)
00960             error = error1;
00961     }
00962 
00963     if (result._exponent + exp > CPDecimalMaxExponent)
00964     {
00965         // This should almost never happen
00966         result._isNaN = YES;
00967         return CPCalculationOverflow;
00968     }
00969     result._exponent += exp;
00970 
00971     // perform round to CPDecimalMaxDigits
00972     if (result._mantissa.length > CPDecimalMaxDigits && !powerMode)
00973     {
00974         result._isCompact = NO;
00975         var scale = CPDecimalMaxDigits - (result._mantissa.length + result._exponent);
00976         CPDecimalRound(result, result, scale, roundingMode); // calls compact
00977 
00978         error = CPCalculationLossOfPrecision;
00979     }
00980     return error;
00981 }
00982 
00992 function CPDecimalMultiply(result, leftOperand, rightOperand, roundingMode, powerMode)
00993 {
00994     var error = CPCalculationNoError,
00995         exp = leftOperand._exponent + rightOperand._exponent,
00996         neg = (leftOperand._isNegative != rightOperand._isNegative);
00997 
00998     if (leftOperand._isNaN || rightOperand._isNaN)
00999     {
01000         result._isNaN = YES;
01001         return CPCalculationNoError;
01002     }
01003 
01004     // check for zero
01005     if (CPDecimalIsZero(rightOperand) || CPDecimalIsZero(leftOperand))
01006     {
01007         _CPDecimalSetZero(result);
01008         return CPCalculationNoError;
01009     }
01010 
01011     //FIXME: Should also check for one
01012 
01013     if (exp > CPDecimalMaxExponent)
01014     {
01015         result._isNaN = YES;
01016         if (neg)
01017             return CPCalculationUnderflow;
01018         else
01019             return CPCalculationOverflow;
01020     }
01021 
01022     var n1 = CPDecimalCopy(leftOperand),
01023         n2 = CPDecimalCopy(rightOperand);
01024 
01025     n1._exponent = 0;
01026     n2._exponent = 0;
01027     n1._isNegative = NO;
01028     n2._isNegative = NO;
01029 
01030     // below is equiv. of simple compare
01031     var comp = 0,
01032         ll = n1._mantissa.length,
01033         lr = n2._mantissa.length;
01034     if (ll == lr)
01035         comp = CPOrderedSame;
01036     else if (ll > lr)
01037         comp = CPOrderedDescending;
01038     else
01039         comp = CPOrderedAscending;
01040 
01041 
01042     if (comp == CPOrderedDescending)
01043     {
01044         error = _SimpleMultiply(result, n1, n2, roundingMode, powerMode);
01045     }
01046     else
01047     {
01048         error = _SimpleMultiply(result, n2, n1, roundingMode, powerMode);
01049     }
01050 
01051     CPDecimalCompact(result);
01052 
01053     if (result._exponent + exp > CPDecimalMaxExponent)
01054     {
01055         result._isNaN = YES;
01056         if (neg)
01057             return CPCalculationUnderflow;
01058         else
01059             return CPCalculationOverflow;
01060     }
01061     else if (result._exponent + exp < CPDecimalMinExponent)
01062     {
01063         // We must cut off some digits
01064         CPDecimalRound(result, result, exp + CPDecimalMaxExponent + 1, roundingMode);
01065         error = CPCalculationLossOfPrecision;
01066 
01067         if (result._exponent + exp < CPDecimalMinExponent)
01068         {
01069             _CPDecimalSetZero(result);
01070             return error;
01071         }
01072     }
01073 
01074     result._exponent += exp;
01075     result._isNegative = neg;
01076 
01077     return error;
01078 }
01079 
01089 function CPDecimalMultiplyByPowerOf10(result, dcm, power, roundingMode)
01090 {
01091     _CPDecimalSet(result, dcm);
01092     var p = result._exponent + power;
01093     if (p > CPDecimalMaxExponent)
01094     {
01095         result._isNaN = YES;
01096         return CPCalculationOverflow;
01097     }
01098     if (p < CPDecimalMinExponent)
01099     {
01100         result._isNaN = YES;
01101         return CPCalculationUnderflow;
01102     }
01103     result._exponent += power;
01104     return CPCalculationNoError;
01105 }
01106 
01116 function CPDecimalPower(result, dcm, power, roundingMode)
01117 {
01118     var error = CPCalculationNoError,
01119         neg = (dcm._isNegative && (power % 2)),
01120         n1 = CPDecimalCopy(dcm);
01121 
01122     n1._isNegative = NO;
01123 
01124     _CPDecimalSetOne(result);
01125 
01126     var e = power;
01127     while (e)
01128     {
01129         if (e & 1)
01130         {
01131             error = CPDecimalMultiply(result, result, n1, roundingMode); //, YES); // enable for high precision powers
01132         }
01133 
01134         error = CPDecimalMultiply(n1, n1, n1, roundingMode); //, YES); // enable for high precision powers
01135 
01136         e >>= 1;
01137 
01138         if (error > CPCalculationLossOfPrecision)
01139             break;
01140     }
01141 
01142     result._isNegative = neg;
01143 
01144 /*  // enable is powerMode to do finally rounding to Max Digits.
01145     if ([result._mantissa count] > CPDecimalMaxDigits)
01146     {
01147         result._isCompact = NO;
01148         var scale = CPDecimalMaxDigits - ([result._mantissa count] + result._exponent);
01149         CPDecimalRound(result, result, scale ,roundingMode); // calls compact
01150         error = CPCalculationLossOfPrecision;
01151     }
01152 */
01153 
01154     CPDecimalCompact(result);
01155 
01156     return error;
01157 }
01158 
01168 function CPDecimalNormalize(dcm1, dcm2, roundingMode, longMode)
01169 {
01170     var factor = (longMode)?2:1;
01171 
01172     if (dcm1._isNaN || dcm2._isNaN)
01173         return CPCalculationNoError; // FIXME: correct behavior?
01174 
01175     // ensure compact
01176     if (!dcm1._isCompact)
01177         CPDecimalCompact(dcm1);
01178     if (!dcm2._isCompact)
01179         CPDecimalCompact(dcm2);
01180 
01181     if (dcm1._exponent == dcm2._exponent)
01182         return CPCalculationNoError;
01183 
01184     var e1 = dcm1._exponent,
01185         e2 = dcm2._exponent;
01186 
01187     // Add zeros
01188     var l2 = dcm2._mantissa.length,
01189         l1 = dcm1._mantissa.length,
01190         l = 0;
01191 
01192     var e = 0;
01193     if (e2 > e1 && e1 >= 0 && e2 >= 0)
01194         e = e2 - e1;
01195     else if (e2 > e1 && e1 < 0 && e2 >= 0)
01196         e = e2 - e1;
01197     else if (e2 > e1 && e1 < 0 && e2 < 0)
01198         e = e2 - e1;
01199     else if (e2 < e1 && e1 >= 0 && e2 >= 0)
01200         e = e1 - e2;
01201     else if (e2 < e1 && e1 >= 0 && e2 < 0)
01202         e = e1 - e2;
01203     else if (e2 < e1 && e1 < 0 && e2 < 0)
01204         e = e1 - e2;
01205 
01206     if (e2 > e1)
01207         l = MIN((CPDecimalMaxDigits * factor) - l2, e); //(e2 - e1));
01208     else
01209         l = MIN((CPDecimalMaxDigits * factor) - l1, e); //(e1 - e2));
01210 
01211     for (var i = 0; i < l; i++)
01212     {
01213         if (e2 > e1)
01214             Array.prototype.push.call(dcm2._mantissa, 0); //dcm2._mantissa[i + l2] = 0;
01215         else
01216             Array.prototype.push.call(dcm1._mantissa, 0);
01217     }
01218     if (e2 > e1)
01219     {
01220         dcm2._exponent -= l;
01221         dcm2._isCompact = NO;
01222     }
01223     else
01224     {
01225         dcm1._exponent -= l;
01226         dcm1._isCompact = NO;
01227     }
01228 
01229     // has been normalised?
01230     if (l != ABS(e2 - e1))//e2 - e1)
01231     {
01232         // no..
01233         // Round of some digits to increase exponent - will compact too
01234         // One number may become zero after this
01235         if (e2 > e1)
01236         {
01237             CPDecimalRound(dcm1, dcm1, -dcm2._exponent, roundingMode);
01238             l1 = CPDecimalIsZero(dcm1);
01239         }
01240         else
01241         {
01242             CPDecimalRound(dcm2, dcm2, -dcm1._exponent, roundingMode);
01243             l2 = CPDecimalIsZero(dcm2);
01244         }
01245 
01246         if ((dcm1._exponent != dcm2._exponent) && ((!l1) || (!l2)))
01247         {
01248             // Some zeros where cut of again by compacting
01249             if (e2 > e1)
01250             {
01251                 l1 = dcm1._mantissa.length;
01252                 l = MIN((CPDecimalMaxDigits * factor) - l1, ABS(dcm1._exponent - dcm2._exponent));
01253                 for (var i = 0; i < l; i++)
01254                 {
01255                     dcm1._mantissa[i + l1] = 0; // or addObject: ? one faster than other?
01256                 }
01257                 dcm1._isCompact = NO;
01258                 dcm1._exponent = dcm2._exponent;
01259             }
01260             else
01261             {
01262                 l2 = dcm2._mantissa.length;
01263                 l = MIN((CPDecimalMaxDigits * factor) - l2, ABS(dcm2._exponent - dcm1._exponent));
01264                 for (var i = 0; i < l; i++)
01265                 {
01266                     dcm2._mantissa[i + l2] = 0; // or addObject: ? one faster than other?
01267                 }
01268                 dcm2._exponent = dcm1._exponent;
01269                 dcm2._isCompact = NO;
01270             }
01271         }
01272         return CPCalculationLossOfPrecision;
01273     }
01274 
01275     return CPCalculationNoError;
01276 }
01277 
01290 function CPDecimalRound(result, dcm, scale ,roundingMode)
01291 {
01292     if (dcm._isNaN)
01293         return;
01294 
01295     if (!dcm._isCompact)
01296         CPDecimalCompact(dcm);
01297 
01298     // FIXME: check for valid inputs (eg scale etc)
01299 
01300     // FIXME: if in longMode should this double?
01301     if (scale == CPDecimalNoScale)
01302         return;
01303 
01304     _CPDecimalSet(result,dcm);
01305 
01306     var mc = result._mantissa.length,
01307         l = mc + scale + result._exponent;
01308 
01309     if (mc <= l)
01310         return;
01311     else if (l <= 0)
01312     {
01313         _CPDecimalSetZero(result);
01314         return;
01315     } else {
01316         var c = 0,
01317             n = 0,
01318             up = 0;
01319 
01320         // Adjust length and exponent
01321         result._exponent += mc - l;
01322 
01323         switch (roundingMode)
01324         {
01325         case CPRoundDown:
01326             up = result._isNegative;
01327             break;
01328         case CPRoundUp:
01329             up = !result._isNegative;
01330             break;
01331         case CPRoundPlain:
01332             n = result._mantissa[l];
01333             up = (n >= 5);
01334             break;
01335         case _CPRoundHalfDown:
01336             n = result._mantissa[l];
01337             up = (n > 5);
01338             break;
01339         case CPRoundBankers:
01340             n = result._mantissa[l];
01341             if (n > 5)
01342                 up = YES;
01343             else if (n < 5)
01344                 up = NO;
01345             else
01346             {
01347                 if (l == 0)
01348                     c = 0;
01349                 else
01350                     c = result._mantissa[l - 1];
01351                 up = ((c % 2) != 0);
01352             }
01353             break;
01354         default:
01355             up = NO;
01356             break;
01357         }
01358         // cut mantissa
01359         result._mantissa = Array.prototype.slice.call(result._mantissa, 0, l);
01360 
01361         if (up)
01362         {
01363             for (var i = l-1; i >= 0; i--)
01364             {
01365                 if (result._mantissa[i] != 9)
01366                 {
01367                     result._mantissa[i]++;
01368                     break;
01369                 }
01370                 result._mantissa[i] = 0;
01371             }
01372             // Final overflow?
01373             if (i == -1)
01374             {
01375                 // As all digits are zeros, just change the first
01376                 result._mantissa[0] = 1;
01377                 if (result._exponent >= CPDecimalMaxExponent)
01378                 {
01379                     // Overflow in rounding.
01380                     // Add one zero add the end. There must be space as
01381                     // we just cut off some digits.
01382                     Array.prototype.push.call(result._mantissa, 0);
01383                 }
01384                 else
01385                     result._exponent++;
01386             }
01387         }
01388     }
01389 
01390     CPDecimalCompact(result);
01391 }
01392 
01398 function CPDecimalCompact(dcm)
01399 {
01400     // if positive or zero exp leading zeros simply delete, trailing ones u need to increment exponent
01401     if (!dcm || dcm._mantissa.length == 0 || CPDecimalIsNotANumber(dcm) )
01402         return;
01403 
01404 
01405     if (CPDecimalIsZero(dcm))
01406     {
01407         // handle zero number compacting
01408         _CPDecimalSetZero(dcm);
01409         return;
01410     }
01411     // leading zeros, when exponent is zero these mean we need to move our decimal point to compact
01412     // if exp is zero does it make sense to have them? don't think so so delete them
01413     while (dcm._mantissa[0] === 0)
01414     {
01415         Array.prototype.shift.call(dcm._mantissa);
01416     }
01417     // trailing zeros, strip them
01418     while (dcm._mantissa[dcm._mantissa.length - 1] === 0)
01419     {
01420         Array.prototype.pop.call(dcm._mantissa);
01421         dcm._exponent++;
01422         if (dcm._exponent + 1 > CPDecimalMaxExponent)
01423         {
01424           // TODO: test case for this
01425           // overflow if we compact anymore, so don't
01426           break;
01427         }
01428     }
01429 
01430     dcm._isCompact = YES;
01431 }
01432 
01440 function CPDecimalString(dcm, locale)
01441 {
01442     // Cocoa seems to just add all the zeros... this maybe controlled by locale,
01443     // will check.
01444     if (dcm._isNaN)
01445         return @"NaN";
01446 
01447     var string = @"",
01448         i = 0;
01449 
01450     if (dcm._isNegative)
01451         string += "-";
01452 
01453     var k = dcm._mantissa.length,
01454         l = ((dcm._exponent < 0) ? dcm._exponent : 0) + k;
01455 
01456     if (l < 0)
01457     {
01458         // add leading zeros
01459         string += "0.";
01460         for (i = 0; i < ABS(l); i++)
01461         {
01462             string += "0";
01463         }
01464         l = k;
01465     }
01466     else if (l == 0)
01467     {
01468         string += "0";
01469     }
01470 
01471     for (i = 0; i < l; i++)
01472     {
01473         string += dcm._mantissa[i];
01474     }
01475     if (l < k)
01476     {
01477         string += ".";
01478         for (i = l; i < k; i++)
01479         {
01480             string += dcm._mantissa[i];
01481         }
01482     }
01483     for (i = 0; i < dcm._exponent; i++)
01484     {
01485         string += "0";
01486     }
01487 
01488     return string;
01489  /*
01490     // GNUStep
01491     if (dcm._isNaN)
01492         return @"NaN";
01493 
01494     var sep = 0;
01495     if ((locale == nil) || (sep = [locale objectForKey: CPDecimalSeparator]) == nil)
01496         sep = @".";
01497 
01498     if (CPDecimalIsZero(dcm))
01499         return @"0" + sep + "0";
01500 
01501     var string = @"";
01502 
01503     if (dcm._isNegative)
01504         string += "-";
01505 
01506     var len = [dcm._mantissa count],
01507         size = len + dcm._exponent;
01508 
01509     if ((len <= 6) && (0 < size) && (size < 7))
01510     {
01511         // For small numbers use the normal format
01512         var i = 0
01513         for (; i < len; i++)
01514         {
01515             if (size == i)
01516                 string += sep;
01517             d = dcm._mantissa[i];
01518             string += d.toString();
01519         }
01520         for (i = 0; i < dcm._exponent; i++)
01521         {
01522             string += "0";
01523         }
01524     }
01525     else if ((len <= 6) && (0 >= size) && (size > -3))
01526     {
01527         // For small numbers use the normal format
01528         string += "0";
01529         string += sep;
01530 
01531         var i = 0;
01532         for (; i > size; i--)
01533         {
01534             string += "0";
01535         }
01536         for (i = 0; i < len; i++)
01537         {
01538             d = dcm._mantissa[i];
01539             string += d.toString();
01540         }
01541     }
01542     else
01543     {
01544         // Scientific format
01545         var i = 0;
01546         for (; i < len; i++)
01547         {
01548             if (1 == i)
01549                 string += sep;
01550             d = dcm._mantissa[i];
01551             string += d.toString();
01552         }
01553         if (size != 1)
01554         {
01555             //s = [NSString stringWithFormat: @"E%d", size-1];
01556             //[string appendString: s];
01557             string += "E" + (size - 1).toString();
01558         }
01559     }
01560 
01561   return string;
01562   */
01563 }
 All Classes Files Functions Variables Defines