diff --git a/Source/NSDecimalNumber.m b/Source/NSDecimalNumber.m new file mode 100644 index 000000000..5de0fa27c --- /dev/null +++ b/Source/NSDecimalNumber.m @@ -0,0 +1,496 @@ +/* + NSDecimalNumber class + Copyright (C) 2000 Free Software Foundation, Inc. + + Written by: Fred Kiefer + Created: July 2000 + + This file is part of the GNUstep Base Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. + */ + +#include +#include +#include +#include + +static NSDecimalNumberHandler *handler; + +@implementation NSDecimalNumberHandler + ++ (id)defaultDecimalNumberHandler +{ + if (handler == nil) + handler = [[self alloc] initWithRoundingMode: NSRoundPlain + scale: 38 + raiseOnExactness: NO + raiseOnOverflow: YES + raiseOnUnderflow: YES + raiseOnDivideByZero: YES]; + + return handler; +} + ++ (id)decimalNumberHandlerWithRoundingMode:(NSRoundingMode)roundingMode + scale:(short)scale + raiseOnExactness:(BOOL)raiseOnExactness + raiseOnOverflow:(BOOL)raiseOnOverflow + raiseOnUnderflow:(BOOL)raiseOnUnderflow + raiseOnDivideByZero:(BOOL)raiseOnDivideByZero +{ + return AUTORELEASE([[self alloc] initWithRoundingMode: roundingMode + scale: scale + raiseOnExactness: raiseOnExactness + raiseOnOverflow: raiseOnOverflow + raiseOnUnderflow: raiseOnUnderflow + raiseOnDivideByZero: raiseOnDivideByZero]); +} + +- (id)initWithRoundingMode:(NSRoundingMode)roundingMode + scale:(short)scale + raiseOnExactness:(BOOL)raiseOnExactness + raiseOnOverflow:(BOOL)raiseOnOverflow + raiseOnUnderflow:(BOOL)raiseOnUnderflow + raiseOnDivideByZero:(BOOL)raiseOnDivideByZero +{ + _roundingMode = roundingMode; + _scale = scale; + _raiseOnExactness = raiseOnExactness; + _raiseOnOverflow = raiseOnOverflow; + _raiseOnUnderflow = raiseOnUnderflow; + _raiseOnDivideByZero = raiseOnDivideByZero; + + return self; +} + +- (NSDecimalNumber*) exceptionDuringOperation: (SEL)method + error: (NSCalculationError)error + leftOperand: (NSDecimalNumber*)leftOperand + rightOperand: (NSDecimalNumber*)rightOperand +{ + switch (error) + { + case NSCalculationNoError: return nil; + case NSCalculationUnderflow: + if (_raiseOnUnderflow) + // FIXME: What exception to raise? + [NSException raise: @"NSDecimalNumberException" format: @"Underflow"]; + else + return [NSDecimalNumber minimumDecimalNumber]; + break; + case NSCalculationOverflow: + if (_raiseOnOverflow) + [NSException raise: @"NSDecimalNumberException" format: @"Overflow"]; + else + return [NSDecimalNumber maximumDecimalNumber]; + break; + case NSCalculationLossOfPrecision: + if (_raiseOnExactness) + [NSException raise: @"NSDecimalNumberException" format: @"Loss of precision"]; + else + return nil; + break; + case NSCalculationDivideByZero: + if (_raiseOnDivideByZero) + [NSException raise: @"NSDecimalNumberException" format: @"Divide by zero"]; + else + return [NSDecimalNumber notANumber]; + break; + } + + return nil; +} + +- (NSRoundingMode) roundingMode +{ + return _roundingMode; +} + +- (short) scale +{ + return _scale; +} + +@end + + +@implementation NSDecimalNumber + +static NSDecimalNumber *maxNumber; +static NSDecimalNumber *minNumber; +static NSDecimalNumber *notANumber; +static NSDecimalNumber *zero; +static NSDecimalNumber *one; + ++ (void) initialize +{ + NSDecimal d; + + d.validNumber = NO; + notANumber = [[self alloc] initWithDecimal: d]; + NSDecimalMax(&d); + maxNumber = [[self alloc] initWithDecimal: d]; + NSDecimalMin(&d); + minNumber = [[self alloc] initWithDecimal: d]; + zero = [[self alloc] initWithMantissa: 0 + exponent: 0 + isNegative: NO]; + one = [[self alloc] initWithMantissa: 1 + exponent: 0 + isNegative: NO]; +} + ++ (id )defaultBehavior +{ + // Reuse the handler from the class NSDecimalNumberHandler + return [NSDecimalNumberHandler defaultDecimalNumberHandler]; +} + ++ (void)setDefaultBehavior:(id )behavior +{ + // Reuse the handler from the class NSDecimalNumberHandler + // Might give interessting result on this class as behavior may came + // from a different class + ASSIGN(handler, behavior); +} + ++ (NSDecimalNumber *)maximumDecimalNumber +{ + return maxNumber; +} ++ (NSDecimalNumber *)minimumDecimalNumber +{ + return minNumber; +} + ++ (NSDecimalNumber *)notANumber +{ + return notANumber; +} + ++ (NSDecimalNumber *)zero +{ + return zero; +} + ++ (NSDecimalNumber *)one +{ + return one; +} + ++ (NSDecimalNumber *)decimalNumberWithDecimal:(NSDecimal)decimal +{ + return AUTORELEASE([[self alloc] initWithDecimal: decimal]); +} + ++ (NSDecimalNumber *)decimalNumberWithMantissa:(unsigned long long)mantissa + exponent:(short)exponent + isNegative:(BOOL)isNegative +{ + return AUTORELEASE([[self alloc] initWithMantissa: mantissa + exponent: exponent + isNegative: isNegative]); +} + ++ (NSDecimalNumber *)decimalNumberWithString:(NSString *)numericString +{ + return AUTORELEASE([[self alloc] initWithString: numericString]); +} + ++ (NSDecimalNumber *)decimalNumberWithString:(NSString *)numericString + locale:(NSDictionary *)locale +{ + return AUTORELEASE([[self alloc] initWithString: numericString + locale: locale]); +} + + +- (id)initWithDecimal:(NSDecimal)decimal +{ + NSDecimalCopy(&data, &decimal); + return self; +} + +- (id)initWithMantissa:(unsigned long long)mantissa + exponent:(short)exponent + isNegative:(BOOL)flag +{ + NSDecimal decimal; + + NSDecimalFromComponents(&decimal, mantissa, exponent, flag); + return [self initWithDecimal: decimal]; +} + +- (id)initWithString:(NSString *)numberValue +{ + NSUserDefaults *defs = [NSUserDefaults standardUserDefaults]; + + return [self initWithString: numberValue + locale: [defs dictionaryRepresentation]]; +} + +- (id)initWithString:(NSString *)numberValue + locale:(NSDictionary *)locale; +{ + NSDecimal decimal; + + NSDecimalFromString(&decimal, numberValue, locale); + return [self initWithDecimal: decimal]; +} + +- (NSString *)descriptionWithLocale:(NSDictionary *)locale +{ + return NSDecimalString(&data, locale); +} + +- (const char *)objCType +{ + return "d"; +} + +- (NSDecimal)decimalValue +{ + NSDecimal decimal; + + NSDecimalCopy(&decimal, &data); + return decimal; +} + +- (double)doubleValue +{ + return NSDecimalDouble(&data); +} + +- (NSComparisonResult)compare:(NSNumber *)decimalNumber +{ + if ([decimalNumber isMemberOfClass: [self class]]) + { + NSDecimal d1 = [self decimalValue]; + NSDecimal d2 = [(NSDecimalNumber*)decimalNumber decimalValue]; + + return NSDecimalCompare(&d1, &d2); + } + else + return [super compare: decimalNumber]; +} + +- (NSDecimalNumber *)decimalNumberByAdding:(NSDecimalNumber *)decimalNumber +{ + return [self decimalNumberByAdding: decimalNumber + withBehavior: [isa defaultBehavior]]; +} + +- (NSDecimalNumber *)decimalNumberByAdding:(NSDecimalNumber *)decimalNumber + withBehavior:(id)behavior +{ + NSDecimal result; + NSDecimal d1 = [self decimalValue]; + NSDecimal d2 = [decimalNumber decimalValue]; + NSCalculationError error; + NSDecimalNumber *res; + + error = NSDecimalAdd(&result, &d1, &d2, [behavior roundingMode]); + if (error) + { + res = [behavior exceptionDuringOperation: _cmd + error: error + leftOperand: self + rightOperand: decimalNumber]; + if (res != nil) + return res; + } + + return [NSDecimalNumber decimalNumberWithDecimal: result]; +} + +- (NSDecimalNumber *)decimalNumberBySubtracting:(NSDecimalNumber *)decimalNumber +{ + return [self decimalNumberBySubtracting: decimalNumber + withBehavior: [isa defaultBehavior]]; +} + +- (NSDecimalNumber *)decimalNumberBySubtracting:(NSDecimalNumber *)decimalNumber + withBehavior:(id )behavior +{ + NSDecimal result; + NSDecimal d1 = [self decimalValue]; + NSDecimal d2 = [decimalNumber decimalValue]; + NSCalculationError error; + NSDecimalNumber *res; + + error = NSDecimalSubtract(&result, &d1, &d2, [behavior roundingMode]); + if (error) + { + res = [behavior exceptionDuringOperation: _cmd + error: error + leftOperand: self + rightOperand: decimalNumber]; + if (res != nil) + return res; + } + + return [NSDecimalNumber decimalNumberWithDecimal: result]; +} + +- (NSDecimalNumber *)decimalNumberByMultiplyingBy:(NSDecimalNumber *)decimalNumber +{ + return [self decimalNumberByMultiplyingBy: decimalNumber + withBehavior: [isa defaultBehavior]]; +} + +- (NSDecimalNumber *)decimalNumberByMultiplyingBy:(NSDecimalNumber *)decimalNumber + withBehavior:(id )behavior +{ + NSDecimal result; + NSDecimal d1 = [self decimalValue]; + NSDecimal d2 = [decimalNumber decimalValue]; + NSCalculationError error; + NSDecimalNumber *res; + + error = NSDecimalMultiply(&result, &d1, &d2, [behavior roundingMode]); + if (error) + { + res = [behavior exceptionDuringOperation: _cmd + error: error + leftOperand: self + rightOperand: decimalNumber]; + if (res != nil) + return res; + } + + return [NSDecimalNumber decimalNumberWithDecimal: result]; +} + +- (NSDecimalNumber *)decimalNumberByDividingBy:(NSDecimalNumber *)decimalNumber +{ + return [self decimalNumberByDividingBy: decimalNumber + withBehavior: [isa defaultBehavior]]; +} + +- (NSDecimalNumber *)decimalNumberByDividingBy:(NSDecimalNumber *)decimalNumber + withBehavior:(id )behavior +{ + NSDecimal result; + NSDecimal d1 = [self decimalValue]; + NSDecimal d2 = [decimalNumber decimalValue]; + NSCalculationError error; + NSDecimalNumber *res; + + error = NSDecimalDivide(&result, &d1, &d2, [behavior roundingMode]); + if (error) + { + res = [behavior exceptionDuringOperation: _cmd + error: error + leftOperand: self + rightOperand: decimalNumber]; + if (res != nil) + return res; + } + + return [NSDecimalNumber decimalNumberWithDecimal: result]; +} + +- (NSDecimalNumber *)decimalNumberByMultiplyingByPowerOf10:(short)power +{ + return [self decimalNumberByMultiplyingByPowerOf10: power + withBehavior: [isa defaultBehavior]]; +} + +- (NSDecimalNumber *)decimalNumberByMultiplyingByPowerOf10:(short)power + withBehavior:(id )behavior +{ + NSDecimal result; + NSDecimal d1 = [self decimalValue]; + NSCalculationError error; + NSDecimalNumber *res; + + error = NSDecimalMultiplyByPowerOf10(&result, &d1, + power, [behavior roundingMode]); + if (error) + { + res = [behavior exceptionDuringOperation: _cmd + error: error + leftOperand: self + rightOperand: nil]; + if (res != nil) + return res; + } + + return [NSDecimalNumber decimalNumberWithDecimal: result]; +} + +- (NSDecimalNumber *)decimalNumberByRaisingToPower:(unsigned)power +{ + return [self decimalNumberByRaisingToPower: power + withBehavior: [isa defaultBehavior]]; +} + +- (NSDecimalNumber *)decimalNumberByRaisingToPower:(unsigned)power + withBehavior:(id )behavior +{ + NSDecimal result; + NSDecimal d1 = [self decimalValue]; + NSCalculationError error; + NSDecimalNumber *res; + + error = NSDecimalPower(&result, &d1, + power, [behavior roundingMode]); + if (error) + { + res = [behavior exceptionDuringOperation: _cmd + error: error + leftOperand: self + rightOperand: nil]; + if (res != nil) + return res; + } + + return [NSDecimalNumber decimalNumberWithDecimal: result]; +} + +- (NSDecimalNumber *)decimalNumberByRoundingAccordingToBehavior:(id )behavior +{ + NSDecimal result; + NSDecimal d1 = [self decimalValue]; + + NSDecimalRound(&result, &d1, [behavior scale], [behavior roundingMode]); + return [NSDecimalNumber decimalNumberWithDecimal: result]; +} + + +// Methods for NSDecimalNumberBehaviors +- (NSDecimalNumber*) exceptionDuringOperation: (SEL)method + error: (NSCalculationError)error + leftOperand: (NSDecimalNumber*)leftOperand + rightOperand: (NSDecimalNumber*)rightOperand +{ + return [[isa defaultBehavior] exceptionDuringOperation: method + error: error + leftOperand: leftOperand + rightOperand: rightOperand]; +} + +- (NSRoundingMode) roundingMode +{ + return [[isa defaultBehavior] roundingMode]; +} + +- (short) scale +{ + return [[isa defaultBehavior] scale]; +} + +@end