Merge branch 'CPDecimalNumber'

This commit is contained in:
Klaas Pieter Annema committed 2010-11-09 10:38:58 +01:00
commit b7d7b99a82
6 files changed
+3559

No files matched your search

File diff suppressed because it is too large. Load diff
+702
View File
@@ -0,0 +1,702 @@
/*
* CPDecimalNumber.j
* Foundation
*
* Created by Stephen Paul Ierodiaconou
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPObject.j"
@import "CPDecimal.j"
@import "CPException.j"
/* The protocol is defined as : Make sure CPDecimalNumberHandler implements these methods
@protocol CPDecimalNumberBehaviors
- (CPRoundingMode)roundingMode;
- (short)scale;
- (CPDecimalNumber)exceptionDuringOperation:(SEL)operation error:(CPCalculationError)error leftOperand:(CPDecimalNumber)leftOperand rightOperand:(CPDecimalNumber)rightOperand;
@end
*/
/*! @class CPDecimalNumberHandler
@ingroup foundation
@brief Decimal floating point number exception and rounding behavior. This
class is mutable.
*/
// protocols this implements <CPCoding, CPDecimalNumberBehaviors>
@implementation CPDecimalNumberHandler : CPObject
{
CPRoundingMode _roundingMode;
short _scale;
BOOL _raiseOnExactness;
BOOL _raiseOnOverflow;
BOOL _raiseOnUnderflow;
BOOL _raiseOnDivideByZero;
}
// initializers
- (id)init
{
if (self = [super init])
{
}
return self;
}
- (id)initWithRoundingMode:(CPRoundingMode)roundingMode scale:(short)scale raiseOnExactness:(BOOL)exact raiseOnOverflow:(BOOL)overflow raiseOnUnderflow:(BOOL)underflow raiseOnDivideByZero:(BOOL)divideByZero
{
if (self = [self init])
{
_roundingMode = roundingMode;
_scale = scale;
_raiseOnExactness = exact;
_raiseOnOverflow = overflow;
_raiseOnUnderflow = underflow;
_raiseOnDivideByZero = divideByZero;
}
return self;
}
// class methods
+ (id)decimalNumberHandlerWithRoundingMode:(CPRoundingMode)roundingMode scale:(short)scale raiseOnExactness:(BOOL)exact raiseOnOverflow:(BOOL)overflow raiseOnUnderflow:(BOOL)underflow raiseOnDivideByZero:(BOOL)divideByZero
{
return [[self alloc] initWithRoundingMode:roundingMode scale:scale raiseOnExactness:exact raiseOnOverflow:overflow raiseOnUnderflow:underflow raiseOnDivideByZero:divideByZero];
}
+ (id)defaultDecimalNumberHandler
{
return _cappdefaultDcmHandler;
}
// CPCoding methods
- (void)encodeWithCoder:(CPCoder)aCoder
{
}
- (id)initWithCoder:(CPCoder)aDecoder
{
}
// CPDecimalNumberBehaviors methods
- (CPRoundingMode)roundingMode
{
return _roundingMode;
}
- (short)scale
{
return _scale;
}
// FIXME: LOCALE?
- (CPDecimalNumber)exceptionDuringOperation:(SEL)operation error:(CPCalculationError)error leftOperand:(CPDecimalNumber)leftOperand rightOperand:(CPDecimalNumber)rightOperand
{
// default behavior of throwing exceptions a la gnustep
switch (error)
{
case CPCalculationNoError:
return nil;
case CPCalculationOverflow:
if (_raiseOnOverflow)
[CPException raise:CPDecimalNumberOverflowException reason:("A CPDecimalNumber overflow has occured. (Left operand= '" + [leftOperand descriptionWithLocale:nil] + "' Right operand= '" + [rightOperand descriptionWithLocale:nil] + "' Selector= '" + operation + "')") ];
else
return [CPDecimalNumber maximumDecimalNumber]; // there was overflow return largest possible val
break;
case CPCalculationUnderflow:
if (_raiseOnUnderflow)
[CPException raise:CPDecimalNumberUnderflowException reason:("A CPDecimalNumber underflow has occured. (Left operand= '" + [leftOperand descriptionWithLocale:nil] + "' Right operand= '" + [rightOperand descriptionWithLocale:nil] + "' Selector= '" + operation + "')") ];
else
return [CPDecimalNumber minimumDecimalNumber]; // there was underflow, return smallest possible value
break;
case CPCalculationLossOfPrecision:
if (_raiseOnExactness)
[CPException raise:CPDecimalNumberExactnessException reason:("A CPDecimalNumber has been rounded off during a calculation. (Left operand= '" + [leftOperand descriptionWithLocale:nil] + "' Right operand= '" + [rightOperand descriptionWithLocale:nil] + "' Selector= '" + operation + "')") ];
else
return nil; // Ignore.
break;
case CPCalculationDivideByZero:
if (_raiseOnDivideByZero)
[CPException raise:CPDecimalNumberDivideByZeroException reason:("A CPDecimalNumber divide by zero has occured. (Left operand= '" + [leftOperand descriptionWithLocale:nil] + "' Right operand= '" + [rightOperand descriptionWithLocale:nil] + "' Selector= '" + operation + "')") ];
else
return [CPDecimalNumber notANumber]; // Div by zero returns NaN
break;
default:
[CPException raise:CPInvalidArgumentException reason:("An unknown CPDecimalNumber error has occured. (Left operand= '" + [leftOperand descriptionWithLocale:nil] + "' Right operand= '" + [rightOperand descriptionWithLocale:nil] + "' Selector= '" + operation + "')") ];
}
return nil;
}
@end
// create the default global behavior class
_cappdefaultDcmHandler = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:CPRoundPlain scale:0 raiseOnExactness:NO raiseOnOverflow:YES raiseOnUnderflow:YES raiseOnDivideByZero:YES];
// After discussing with boucher about this it makes sense not to inherit, as
// CPNumber is toll free bridged and anyway its methods will need to be
// overrided here as CPDecimalNumber is not interchangable with CPNumbers.
/*! @class CPDecimalNumber
@ingroup foundation
@brief Decimal floating point number
This class represents a decimal floating point number and the relavent
mathematical operations to go with it.
This class is mutable.
*/
@implementation CPDecimalNumber : CPNumber
{
CPDecimal _data;
}
+ (id)alloc
{
return class_createInstance(self);
}
// initializers
- (id)init
{
if (self = [super init])
{}
return self;
}
/*!
Initialise a CPDecimalNumber object with the contents of a CPDecimal object
@param dcm the CPDecimal object to copy
@return the reference to the receiver CPDecimalNumber
*/
- (id)initWithDecimal:(CPDecimal)dcm
{
if (self = [self init])
_data = CPDecimalCopy(dcm);
return self;
}
// NOTE: long long doesnt exist in JS, so this is actually a double
- (id)initWithMantissa:(unsigned long long)mantissa exponent:(short)exponent isNegative:(BOOL)flag
{
if (self = [self init])
{
if( flag )
mantissa *= -1;
_data = CPDecimalMakeWithParts(mantissa, exponent);
}
return self;
}
- (id)initWithString:(CPString)numberValue
{
if (self = [self init])
{
_data = CPDecimalMakeWithString(numberValue, nil);
if (_data === nil)
[CPException raise:CPInvalidArgumentException reason:"A CPDecimalNumber has been passed an invalid string. '" + numberValue + "'"];
}
return self;
}
- (id)initWithString:(CPString)numberValue locale:(CPDictionary)locale
{
if (self = [self init])
{
_data = CPDecimalMakeWithString(numberValue,locale);
if (!_data)
[CPException raise:CPInvalidArgumentException reason:"A CPDecimalNumber has been passed an invalid string. '" + numberValue + "'"];
}
return self;
}
// class methods
+ (CPDecimalNumber)decimalNumberWithDecimal:(CPDecimal)dcm
{
return [[self alloc] initWithDecimal:dcm];
}
+ (CPDecimalNumber)decimalNumberWithMantissa:(unsigned long long)mantissa exponent:(short)exponent isNegative:(BOOL)flag
{
return [[self alloc] initWithMantissa:mantissa exponent:exponent isNegative:flag];
}
+ (CPDecimalNumber)decimalNumberWithString:(CPString)numberValue
{
return [[self alloc] initWithString:numberValue];
}
+ (CPDecimalNumber)decimalNumberWithString:(CPString)numberValue locale:(CPDictionary)locale
{
return [[self alloc] initWithString:numberValue locale:locale];
}
+ (id)defaultBehavior
{
return _cappdefaultDcmHandler;
}
+ (CPDecimalNumber)maximumDecimalNumber
{
var s = @"",
i = 0;
for(;i < CPDecimalMaxDigits; i++)
s += "9";
s += "e" + CPDecimalMaxExponent;
return [[self alloc] initWithString:s];
}
+ (CPDecimalNumber)minimumDecimalNumber
{
var s = @"-",
i = 0;
for(;i < CPDecimalMaxDigits; i++)
s += "9";
s += "e" + CPDecimalMinExponent;
return [[self alloc] initWithString:s];
}
+ (CPDecimalNumber)notANumber
{
return [[self alloc] initWithDecimal:CPDecimalMakeNaN()];
}
+ (CPDecimalNumber)one
{
return [[self alloc] initWithString:"1"];
}
+ (void)setDefaultBehavior:(id <CPDecimalNumberBehaviors>)behavior
{
_cappdefaultDcmHandler = behavior;
}
+ (CPDecimalNumber)zero
{
return [[self alloc] initWithString:"0"];
}
// instance methods
- (CPDecimalNumber)decimalNumberByAdding:(CPDecimalNumber)decimalNumber
{
return [self decimalNumberByAdding:decimalNumber withBehavior:_cappdefaultDcmHandler];
}
- (CPDecimalNumber)decimalNumberByAdding:(CPDecimalNumber)decimalNumber withBehavior:(id <CPDecimalNumberBehaviors>)behavior
{
// FIXME: Surely this can take CPNumber (any JS number) as an argument as CPNumber is CPDecimalNumbers super normally (not here tho)
var result = CPDecimalMakeZero(),
res = 0,
error = CPDecimalAdd(result, [self decimalValue], [decimalNumber decimalValue], [behavior roundingMode]);
if (error > CPCalculationNoError)
{
res = [behavior exceptionDuringOperation:_cmd error:error leftOperand:self rightOperand:decimalNumber];
// Gnustep does this, not sure if it is correct behavior
if (res != nil)
return res; // say on overflow and no exception handling, returns max decimal val
}
return [CPDecimalNumber decimalNumberWithDecimal:result];
}
- (CPDecimalNumber)decimalNumberBySubtracting:(CPDecimalNumber)decimalNumber
{
return [self decimalNumberBySubtracting:decimalNumber withBehavior:_cappdefaultDcmHandler];
}
- (CPDecimalNumber)decimalNumberBySubtracting:(CPDecimalNumber)decimalNumber withBehavior:(id <CPDecimalNumberBehaviors>)behavior
{
var result = CPDecimalMakeZero(),
error = CPDecimalSubtract(result, [self decimalValue], [decimalNumber decimalValue], [behavior roundingMode]);
if (error > CPCalculationNoError)
{
var res = [behavior exceptionDuringOperation:_cmd error:error leftOperand:self rightOperand:decimalNumber];
// Gnustep does this, not sure if it is correct behavior
if (res != nil)
return res; // say on overflow and no exception handling, returns max decimal val
}
return [CPDecimalNumber decimalNumberWithDecimal:result];
}
- (CPDecimalNumber)decimalNumberByDividingBy:(CPDecimalNumber)decimalNumber
{
return [self decimalNumberByDividingBy:decimalNumber withBehavior:_cappdefaultDcmHandler];
}
- (CPDecimalNumber)decimalNumberByDividingBy:(CPDecimalNumber)decimalNumber withBehavior:(id <CPDecimalNumberBehaviors>)behavior
{
var result = CPDecimalMakeZero(),
error = CPDecimalDivide(result, [self decimalValue], [decimalNumber decimalValue], [behavior roundingMode]);
if (error > CPCalculationNoError)
{
var res = [behavior exceptionDuringOperation:_cmd error:error leftOperand:self rightOperand:decimalNumber];
// Gnustep does this, not sure if it is correct behavior
if (res != nil)
return res; // say on overflow and no exception handling, returns max decimal val
}
return [CPDecimalNumber decimalNumberWithDecimal:result];
}
- (CPDecimalNumber)decimalNumberByMultiplyingBy:(CPDecimalNumber)decimalNumber
{
return [self decimalNumberByMultiplyingBy:decimalNumber withBehavior:_cappdefaultDcmHandler];
}
- (CPDecimalNumber)decimalNumberByMultiplyingBy:(CPDecimalNumber)decimalNumber withBehavior:(id <CPDecimalNumberBehaviors>)behavior
{
var result = CPDecimalMakeZero(),
error = CPDecimalMultiply(result, [self decimalValue], [decimalNumber decimalValue], [behavior roundingMode]);
if (error > CPCalculationNoError)
{
var res = [behavior exceptionDuringOperation:_cmd error:error leftOperand:self rightOperand:decimalNumber];
// Gnustep does this, not sure if it is correct behavior
if (res != nil)
return res; // say on overflow and no exception handling, returns max decimal val
}
return [CPDecimalNumber decimalNumberWithDecimal:result];
}
- (CPDecimalNumber)decimalNumberByMultiplyingByPowerOf10:(short)power
{
return [self decimalNumberByMultiplyingByPowerOf10:power withBehavior:_cappdefaultDcmHandler];
}
- (CPDecimalNumber)decimalNumberByMultiplyingByPowerOf10:(short)power withBehavior:(id <CPDecimalNumberBehaviors>)behavior
{
var result = CPDecimalMakeZero(),
error = CPDecimalMultiplyByPowerOf10(result, [self decimalValue], power, [behavior roundingMode]);
if (error > CPCalculationNoError)
{
var res = [behavior exceptionDuringOperation:_cmd error:error leftOperand:self rightOperand:[CPDecimalNumber decimalNumberWithString:power.toString()]];
// Gnustep does this, not sure if it is correct behavior
if (res != nil)
return res; // say on overflow and no exception handling, returns max decimal val
}
return [CPDecimalNumber decimalNumberWithDecimal:result];
}
- (CPDecimalNumber)decimalNumberByRaisingToPower:(unsigned)power
{
return [self decimalNumberByRaisingToPower:power withBehavior:_cappdefaultDcmHandler];
}
- (CPDecimalNumber)decimalNumberByRaisingToPower:(unsigned)power withBehavior:(id <CPDecimalNumberBehaviors>)behavior
{
if (power < 0)
return [behavior exceptionDuringOperation:_cmd error:-1 leftOperand:self rightOperand:[CPDecimalNumber decimalNumberWithString:power.toString()]];
var result = CPDecimalMakeZero(),
error = CPDecimalPower(result, [self decimalValue], power, [behavior roundingMode]);
if (error > CPCalculationNoError)
{
var res = [behavior exceptionDuringOperation:_cmd error:error leftOperand:self rightOperand:[CPDecimalNumber decimalNumberWithString:power.toString()]];
// Gnustep does this, not sure if it is correct behavior
if (res != nil)
return res; // say on overflow and no exception handling, returns max decimal val
}
return [CPDecimalNumber decimalNumberWithDecimal:result];
}
- (CPDecimalNumber)decimalNumberByRoundingAccordingToBehavior:(id <CPDecimalNumberBehaviors>)behavior
{
var result = CPDecimalMakeZero();
CPDecimalRound(result, [self decimalValue], [behavior scale], [behavior roundingMode]);
return [CPDecimalNumber decimalNumberWithDecimal:result];
}
// This method takes a CPNumber. Thus the parameter may be a CPDecimalNumber or a CPNumber class.
// Thus the type is checked to send the operand to the correct compare function.
- (CPComparisonResult)compare:(CPNumber)decimalNumber
{
if ([decimalNumber class] != CPDecimalNumber)
decimalNumber = [CPDecimalNumber decimalNumberWithString:decimalNumber.toString()];
return CPDecimalCompare([self decimalValue], [decimalNumber decimalValue]);
}
/*!
Unimplemented
*/
- (CPString)hash
{
objj_exception_throw("hash: NOT YET IMPLEMENTED");
}
/*!
The objective C type string. For compatability reasons
@return returns a CPString containing "d"
*/
- (CPString)objCType
{
return @"d";
}
- (CPString)description
{
// FIXME: I expect here locale should be some default locale
return [self descriptionWithLocale:nil]
}
- (CPString)descriptionWithLocale:(CPDictionary)locale
{
return CPDecimalString(_data, locale);
}
- (CPString)stringValue
{
return [self description];
}
/*!
Returns a new CPDecimal object (which effectively contains the
internal decimal number representation).
@return a new CPDecimal number copy
*/
- (CPDecimal)decimalValue
{
return CPDecimalCopy(_data);
}
// Type Conversion Methods
- (double)doubleValue
{
// FIXME: locale support / bounds check?
return parseFloat([self stringValue]);
}
- (BOOL)boolValue
{
return (CPDecimalIsZero(_data))?NO:YES;
}
- (char)charValue
{
// FIXME: locale support / bounds check?
return parseInt([self stringValue]);
}
- (float)floatValue
{
// FIXME: locale support / bounds check?
return parseFloat([self stringValue]);
}
- (int)intValue
{
// FIXME: locale support / bounds check?
return parseInt([self stringValue]);
}
- (long long)longLongValue
{
// FIXME: locale support / bounds check?
return parseFloat([self stringValue]);
}
- (long)longValue
{
// FIXME: locale support / bounds check?
return parseInt([self stringValue]);
}
- (short)shortValue
{
// FIXME: locale support / bounds check?
return parseInt([self stringValue]);
}
- (unsigned char)unsignedCharValue
{
// FIXME: locale support / bounds check?
return parseInt([self stringValue]);
}
- (unsigned int)unsignedIntValue
{
// FIXME: locale support / bounds check?
return parseInt([self stringValue]);
}
- (unsigned long)unsignedLongValue
{
// FIXME: locale support / bounds check?
return parseInt([self stringValue]);
}
- (unsigned short)unsignedShortValue
{
// FIXME: locale support / bounds check?
return parseInt([self stringValue]);
}
- (BOOL)isEqualToNumber:(CPNumber)aNumber
{
return (CPDecimalCompare(CPDecimalMakeWithString(aNumber.toString(),nil), _data) == CPOrderedSame)?YES:NO;
}
// CPNumber inherited methods
+ (id)numberWithBool:(BOOL)aBoolean
{
return [[self alloc] initWithBool:aBoolean];
}
+ (id)numberWithChar:(char)aChar
{
return [[self alloc] initWithChar:aChar];
}
+ (id)numberWithDouble:(double)aDouble
{
return [[self alloc] initWithDouble:aDouble];
}
+ (id)numberWithFloat:(float)aFloat
{
return [[self alloc] initWithFloat:aFloat];
}
+ (id)numberWithInt:(int)anInt
{
return [[self alloc] initWithInt:anInt];
}
+ (id)numberWithLong:(long)aLong
{
return [[self alloc] initWithLong:aLong];
}
+ (id)numberWithLongLong:(long long)aLongLong
{
return [[self alloc] initWithLongLong:aLongLong];
}
+ (id)numberWithShort:(short)aShort
{
return [[self alloc] initWithShort:aShort];
}
+ (id)numberWithUnsignedChar:(unsigned char)aChar
{
return [[self alloc] initWithUnsignedChar:aChar];
}
+ (id)numberWithUnsignedInt:(unsigned)anUnsignedInt
{
return [[self alloc] initWithUnsignedInt:anUnsignedInt];
}
+ (id)numberWithUnsignedLong:(unsigned long)anUnsignedLong
{
return [[self alloc] initWithUnsignedLong:anUnsignedLong];
}
+ (id)numberWithUnsignedLongLong:(unsigned long)anUnsignedLongLong
{
return [[self alloc] initWithUnsignedLongLong:anUnsignedLongLong];
}
+ (id)numberWithUnsignedShort:(unsigned short)anUnsignedShort
{
return [[self alloc] initWithUnsignedShort:anUnsignedShort];
}
- (id)initWithBool:(BOOL)value
{
if (self = [self init])
_data = CPDecimalMakeWithParts((value)?1:0, 0);
return self;
}
- (id)initWithChar:(char)value
{
return [self _initWithJSNumber:value];
}
- (id)initWithDouble:(double)value
{
return [self _initWithJSNumber:value];
}
- (id)initWithFloat:(float)value
{
return [self _initWithJSNumber:value];
}
- (id)initWithInt:(int)value
{
return [self _initWithJSNumber:value];
}
- (id)initWithLong:(long)value
{
return [self _initWithJSNumber:value];
}
- (id)initWithLongLong:(long long)value
{
return [self _initWithJSNumber:value];
}
- (id)initWithShort:(short)value
{
return [self _initWithJSNumber:value];
}
- (id)initWithUnsignedChar:(unsigned char)value
{
return [self _initWithJSNumber:value];
}
- (id)initWithUnsignedInt:(unsigned)value
{
return [self _initWithJSNumber:value];
}
- (id)initWithUnsignedLong:(unsigned long)value
{
return [self _initWithJSNumber:value];
}
- (id)initWithUnsignedLongLong:(unsigned long long)value
{
return [self _initWithJSNumber:value];
}
- (id)initWithUnsignedShort:(unsigned short)value
{
return [self _initWithJSNumber:value];
}
- (id)_initWithJSNumber:value
{
if (self = [self init])
_data = CPDecimalMakeWithString(value.toString(), nil);
return self;
}
@end
+2
View File
@@ -28,6 +28,8 @@
@import "CPCompoundPredicate.j"
@import "CPData.j"
@import "CPDate.j"
@import "CPDecimal.j"
@import "CPDecimalNumber.j"
@import "CPDictionary.j"
@import "CPEnumerator.j"
@import "CPException.j"
@@ -0,0 +1,149 @@
/*
* CPDecimalNumberHandlerTest.j
* Foundation
*
* Created by Stephen Paul Ierodiaconou
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/CPString.j>
@import <Foundation/CPDecimalNumber.j>
@implementation CPDecimalNumberHandlerTest : OJTestCase
- (void)testinitWithRoundingMode
{
var h1 = [CPDecimalNumberHandler alloc];
[self assertTrue:h1 message:"T1 initWithRoundingMode: failed to alloc"];
[h1 initWithRoundingMode:CPRoundPlain scale:3 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
[self assert:3 equals:[h1 scale] message:"T1 initWithRoundingMode: scale wrong"];
}
- (void)testdecimalNumberHandlerWithRoundingMode
{
var h1 = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:CPRoundPlain scale:0 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
[self assertTrue:h1 message:"T1 decimalNumberHandlerWithRoundingMode: returned nil"];
[self assertTrue:[h1 roundingMode] message:"T1 decimalNumberHandlerWithRoundingMode: rounding mode wrong"];
}
- (void)testdefaultDecimalNumberHandler
{
[self assert:_cappdefaultDcmHandler equals:[CPDecimalNumberHandler defaultDecimalNumberHandler] message:"T1 defaultDecimalNumberHandler returned different handler than the current default"];
}
- (void)testroundingMode
{
var h1 = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:CPRoundDown scale:0 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
[self assertTrue:h1 message:"T1 roundingMode: no alloc"];
[self assert:2 equals:[h1 roundingMode] message:"T1 roundingMode: rounding mode wrong"];
}
- (void)testscale
{
var h1 = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:CPRoundDown scale:-6 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
[self assertTrue:h1 message:"T1 roundingMode: no alloc"];
[self assert:-6 equals:[h1 scale] message:"T1 roundingMode: scale wrong"];
}
- (void)testexceptionDuringOperation
{
var h1 = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:CPRoundDown scale:0 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
[self assertTrue:h1 message:"T1 exceptionDuringOperation: no alloc"];
var a = [CPDecimalNumber decimalNumberWithString:@"100"];
var b = [CPDecimalNumber decimalNumberWithString:@"100"];
// no throw first
try {
[h1 exceptionDuringOperation:nil error:CPCalculationLossOfPrecision leftOperand:a rightOperand:b];
}
catch (e)
{
[self fail:"T2 exceptionDuringOperation: Should have not thrown a precision exception"];
}
try {
[h1 exceptionDuringOperation:nil error:CPCalculationOverflow leftOperand:a rightOperand:b];
}
catch (e)
{
[self fail:"T3 exceptionDuringOperation: Should have not thrown an overflow exception"];
}
try {
[h1 exceptionDuringOperation:nil error:CPCalculationUnderflow leftOperand:a rightOperand:b];
}
catch (e)
{
[self fail:"T4 exceptionDuringOperation: Should have not thrown an underflow exception"];
}
try {
[h1 exceptionDuringOperation:nil error:CPCalculationDivideByZero leftOperand:a rightOperand:b];
}
catch (e)
{
[self fail:"T5 exceptionDuringOperation: Should have not thrown a div by zero exception"];
}
// now throw
h1 = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:CPRoundDown scale:0 raiseOnExactness:YES raiseOnOverflow:YES raiseOnUnderflow:YES raiseOnDivideByZero:YES];
try {
[h1 exceptionDuringOperation:nil error:CPCalculationLossOfPrecision leftOperand:a rightOperand:b];
[self fail:"T6 exceptionDuringOperation: Should have thrown a precision exception"];
}
catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
try {
[h1 exceptionDuringOperation:nil error:CPCalculationOverflow leftOperand:a rightOperand:b];
[self fail:"T3 exceptionDuringOperation: Should have thrown an overflow exception"];
}
catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
try {
[h1 exceptionDuringOperation:nil error:CPCalculationUnderflow leftOperand:a rightOperand:b];
[self fail:"T4 exceptionDuringOperation: Should have thrown an underflow exception"];
}
catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
try {
[h1 exceptionDuringOperation:nil error:CPCalculationDivideByZero leftOperand:a rightOperand:b];
[self fail:"T5 exceptionDuringOperation: Should have thrown a div by zero exception"];
}
catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
}
@end
+535
View File
@@ -0,0 +1,535 @@
/*
* CPDecimalNumberTest.j
* Foundation
*
* Created by Stephen Paul Ierodiaconou
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/CPString.j>
@import <Foundation/CPDecimalNumber.j>
@implementation CPDecimalNumberTest : OJTestCase
{
CPDecimalNumberHandler _dcmnh;
CPDecimalNumberHandler _dcmnhWithExactness;
}
- (void)setUp
{
if (!_dcmnh)
_dcmnh = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:CPRoundPlain scale:0 raiseOnExactness:NO raiseOnOverflow:YES raiseOnUnderflow:YES raiseOnDivideByZero:YES];
if (!_dcmnhWithExactness)
_dcmnhWithExactness = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:CPRoundPlain scale:0 raiseOnExactness:YES raiseOnOverflow:YES raiseOnUnderflow:YES raiseOnDivideByZero:YES];
}
// init
- (void)testInitialisersAndMisc
{
// CPDecimalNumber class initialisers
var dcmn = [[CPDecimalNumber alloc] initWithString:@"10.72342"];
var dcm = [dcmn decimalValue];
[self assert:-5 equals:dcm._exponent message:"initWithString: - exponent"];
[self assert:[1,0,7,2,3,4,2] equals:dcm._mantissa message:"initWithString: - mantissa"];
[self assert:NO equals:dcm._isNegative message:"initWithString: - sign"];
[self assert:NO equals:dcm._isNaN message:"initWithString: - NaN is incorrectly set"];
dcmn = [[CPDecimalNumber alloc] initWithString:@"0.000001e18" locale:nil];
dcm = [dcmn decimalValue];
[self assert:12 equals:dcm._exponent message:"initWithString:locale: - exponent"];
[self assert:[1] equals:dcm._mantissa message:"initWithString:locale: mantissa"];
[self assert:NO equals:dcm._isNegative message:"initWithString:locale: sign"];
[self assert:NO equals:dcm._isNaN message:"initWithString:locale: NaN is incorrectly set"];
dcmn = [CPDecimalNumber decimalNumberWithString:@"-578e-2"];
dcm = [dcmn decimalValue];
[self assert:-2 equals:dcm._exponent message:"decimalNumberWithString: - exponent"];
[self assert:[5,7,8] equals:dcm._mantissa message:"decimalNumberWithString: - mantissa"];
[self assert:YES equals:dcm._isNegative message:"decimalNumberWithString: - sign"];
[self assert:NO equals:dcm._isNaN message:"decimalNumberWithString: - NaN is incorrectly set"];
dcmn = [CPDecimalNumber decimalNumberWithString:@"-0.0001e-5" locale:nil];
dcm = [dcmn decimalValue];
[self assert:-9 equals:dcm._exponent message:"decimalNumberWithString:locale: - exponent"];
[self assert:[1] equals:dcm._mantissa message:"decimalNumberWithString:locale: - mantissa"];
[self assert:YES equals:dcm._isNegative message:"decimalNumberWithString:locale: - sign"];
[self assert:NO equals:dcm._isNaN message:"decimalNumberWithString:locale: - NaN is incorrectly set"];
dcmn = [[CPDecimalNumber alloc] initWithDecimal:dcm];
dcm = [dcmn decimalValue];
[self assert:-9 equals:dcm._exponent message:"initWithDecimal: - exponent"];
[self assert:[1] equals:dcm._mantissa message:"initWithDecimal: - mantissa"];
[self assert:YES equals:dcm._isNegative message:"initWithDecimal: - sign"];
dcmn = [CPDecimalNumber decimalNumberWithDecimal:dcm];
dcm = [dcmn decimalValue];
[self assert:-9 equals:dcm._exponent message:"decimalNumberWithDecimal: - exponent"];
[self assert:[1] equals:dcm._mantissa message:"decimalNumberWithDecimal: - mantissa"];
[self assert:YES equals:dcm._isNegative message:"decimalNumberWithDecimal: - sign"];
dcmn = [[CPDecimalNumber alloc] initWithMantissa:123654 exponent:19 isNegative:YES];
dcm = [dcmn decimalValue];
[self assert:19 equals:dcm._exponent message:"initWithMantissa:exponent:isNegative: - exponent"];
[self assert:[1,2,3,6,5,4] equals:dcm._mantissa message:"initWithMantissa:exponent:isNegative: - mantissa"];
[self assert:YES equals:dcm._isNegative message:"initWithMantissa:exponent:isNegative: - sign"];
dcmn = [CPDecimalNumber decimalNumberWithMantissa:9876543 exponent:2 isNegative:NO];
dcm = [dcmn decimalValue];
[self assert:2 equals:dcm._exponent message:"decimalNumberWithMantissa:exponent:isNegative: - exponent"];
[self assert:[9,8,7,6,5,4,3] equals:dcm._mantissa message:"decimalNumberWithMantissa:exponent:isNegative: - mantissa"];
[self assert:NO equals:dcm._isNegative message:"decimalNumberWithMantissa:exponent:isNegative: - sign"];
// Def behavior
[self assert:[CPDecimalNumber defaultBehavior] equals:_cappdefaultDcmHandler message:"defaultBehavior: - returned different object"];
[CPDecimalNumber setDefaultBehavior:_dcmnhWithExactness];
[self assertTrue:[CPDecimalNumber defaultBehavior]._raiseOnExactness message:"setDefaultBehavior: - new behavior not set"];
[CPDecimalNumber setDefaultBehavior:_dcmnh];
// Range and special values
dcmn = [CPDecimalNumber maximumDecimalNumber];
dcm = [dcmn decimalValue];
[self assert:CPDecimalMaxExponent equals:dcm._exponent message:"maximumDecimalNumber: - exponent"];
[self assert:[9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9] equals:dcm._mantissa message:"maximumDecimalNumber: - mantissa"];
[self assert:NO equals:dcm._isNegative message:"maximumDecimalNumber: - sign"];
dcmn = [CPDecimalNumber minimumDecimalNumber];
dcm = [dcmn decimalValue];
[self assert:CPDecimalMinExponent equals:dcm._exponent message:"minimumDecimalNumber: - exponent"];
[self assert:[9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9] equals:dcm._mantissa message:"minimumDecimalNumber: - mantissa"];
[self assert:YES equals:dcm._isNegative message:"minimumDecimalNumber: - sign"];
dcmn = [CPDecimalNumber notANumber];
dcm = [dcmn decimalValue];
[self assert:YES equals:dcm._isNaN message:"notANumber: - NaN should be YES"];
dcmn = [CPDecimalNumber one];
dcm = [dcmn decimalValue];
[self assert:0 equals:dcm._exponent message:"one: - exponent"];
[self assert:[1] equals:dcm._mantissa message:"one: - mantissa"];
[self assert:NO equals:dcm._isNegative message:"one: - sign"];
dcmn = [CPDecimalNumber zero];
dcm = [dcmn decimalValue];
[self assert:0 equals:dcm._exponent message:"zero: - exponent"];
[self assert:[0] equals:dcm._mantissa message:"zero: - mantissa"];
[self assert:NO equals:dcm._isNegative message:"zero: - sign"];
// misc
// decimalValue <- if it hasnt been working so far, your would know.
dcmn = [CPDecimalNumber zero];
dcm = [dcmn boolValue];
[self assert:false equals:dcm message:"boolValue: - should be false"];
dcmn = [CPDecimalNumber decimalNumberWithString:@"578"];
dcm = [dcmn boolValue];
[self assert:true equals:dcm message:"boolValue: - should be true"];
// exceptions
try {
dcmn = [[CPDecimalNumber alloc] initWithString:@"111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"];
[self fail:"initWithString: TEX1: overflow from string"];
} catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
try {
dcmn = [[CPDecimalNumber alloc] initWithString:@"foo"];
[self fail:"initWithString: TEX2: invalid string"];
} catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
try {
dcmn = [[CPDecimalNumber alloc] initWithString:@".123"];
[self fail:"initWithString: TEX3: invalid string"];
} catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
try {
dcmn = [[CPDecimalNumber alloc] initWithString:@"-.123"];
[self fail:"initWithString: TEX4: invalid string"];
} catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
try {
dcmn = [[CPDecimalNumber alloc] initWithString:@"0123"];
[self fail:"initWithString: TEX5: invalid string"];
} catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
try {
dcmn = [[CPDecimalNumber alloc] initWithString:@"1e200"];
[self fail:"initWithString: TEX6: exponent overflow"];
} catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
try{
dcmn = [[CPDecimalNumber alloc] initWithString:@"12312e-23421"];
[self fail:"initWithString: TEX7: exponent underflow"];
} catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
}
- (void)testAdd
{
var dcmn1 = [CPDecimalNumber decimalNumberWithString:@"123"];
var dcmn2 = [CPDecimalNumber decimalNumberWithString:@"321"];
var dcmn3 = [dcmn1 decimalNumberByAdding:dcmn2];
var d1 = [dcmn3 decimalValue];
[self assert:0 equals:d1._exponent message:"decimalNumberByAdding: - exponent"];
[self assert:[4,4,4] equals:d1._mantissa message:"decimalNumberByAdding: - mantissa"];
[self assert:NO equals:d1._isNegative message:"decimalNumberByAdding: - sign"];
[self assert:NO equals:d1._isNaN message:"decimalNumberByAdding: - NaN is incorrectly set"];
// precision throw - with behavior
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"10000000000000000000000000000000000001"];
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"1.00001"];
try {
dcmn3 = [dcmn1 decimalNumberByAdding:dcmn2 withBehavior:_dcmnhWithExactness];
[self fail:"decimalNumberByAdding:withBehavior: TEX1 - should have throw precision error "];
}
catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
try {
dcmn3 = [dcmn1 decimalNumberByAdding:dcmn2];
}
catch (e)
{
[self fail:"decimalNumberByAdding: TEX2 - should not have thrown precision error "];
}
// overflow
dcmn1 = [CPDecimalNumber maximumDecimalNumber];
dcmn2 = [CPDecimalNumber maximumDecimalNumber];
try {
dcmn3 = [dcmn1 decimalNumberByAdding:dcmn2];
[self fail:"decimalNumberByAdding: TEX3 - should have thrown overflow error "];
}
catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
}
- (void)testSubtract
{
var dcmn1 = [CPDecimalNumber decimalNumberWithString:@"301"];
var dcmn2 = [CPDecimalNumber decimalNumberWithString:@"153"];
var dcmn3 = [dcmn1 decimalNumberBySubtracting:dcmn2];
var d1 = [dcmn3 decimalValue];
[self assert:0 equals:d1._exponent message:"decimalNumberBySubtracting: - exponent"];
[self assert:[1,4,8] equals:d1._mantissa message:"decimalNumberBySubtracting: - mantissa"];
[self assert:NO equals:d1._isNegative message:"decimalNumberBySubtracting: - sign"];
[self assert:NO equals:d1._isNaN message:"decimalNumberBySubtracting: - NaN is incorrectly set"];
// precision throw - with behavior
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"1e-128"];
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"1"];
try {
dcmn3 = [dcmn1 decimalNumberBySubtracting:dcmn2 withBehavior:_dcmnhWithExactness];
[self fail:"decimalNumberBySubtracting:withBehavior: TEX1 - should have throw precision error "];
}
catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
try {
dcmn3 = [dcmn1 decimalNumberBySubtracting:dcmn2];
}
catch (e)
{
[self fail:"decimalNumberBySubtracting: TEX2 - should not have thrown precision error "];
}
// can we get underflow from subtraction since we cant represent anything smaller than the allowed range anyway?
}
- (void)testDivide
{
var dcmn1 = [CPDecimalNumber decimalNumberWithString:@"7126313"];
var dcmn2 = [CPDecimalNumber decimalNumberWithString:@"1235"];
var dcmn3 = [dcmn1 decimalNumberByDividingBy:dcmn2];
var d1 = [dcmn3 decimalValue];
[self assert:-34 equals:d1._exponent message:"decimalNumberByDividingBy: - exponent"];
[self assert:[5,7,7,0,2,9,3,9,2,7,1,2,5,5,0,6,0,7,2,8,7,4,4,9,3,9,2,7,1,2,5,5,0,6,0,7,2,8] equals:d1._mantissa message:"decimalNumberByDividingBy: T1: mantissa"];
[self assert:NO equals:d1._isNegative message:"decimalNumberByDividingBy: - sign"];
[self assert:NO equals:d1._isNaN message:"decimalNumberByDividingBy: - NaN is incorrectly set"];
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"1"];
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"2"];
dcmn3 = [dcmn1 decimalNumberByDividingBy:dcmn2 withBehavior:_dcmnhWithExactness];
d1 = [dcmn3 decimalValue];
[self assert:-1 equals:d1._exponent message:"decimalNumberByDividingBy:withBehavior: - exponent"];
[self assert:[5] equals:d1._mantissa message:"decimalNumberByDividingBy:withBehavior: - mantissa"];
[self assert:NO equals:d1._isNegative message:"decimalNumberByDividingBy:withBehavior: - sign"];
[self assert:NO equals:d1._isNaN message:"decimalNumberByDividingBy:withBehavior: - NaN is incorrectly set"];
// Exceptions
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"1"];
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"3"];
try {
dcmn3 = [dcmn1 decimalNumberByDividingBy:dcmn2 withBehavior:_dcmnhWithExactness];
[self fail:"decimalNumberByDividingBy:withBehavior: TEX1 - should have thrown precision error "];
}
catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
try {
dcmn3 = [dcmn1 decimalNumberByDividingBy:dcmn2];
}
catch (e)
{
[self fail:"decimalNumberByDividingBy: TEX2 - should not have thrown precision error "];
}
// underflow , if dcnm2 is < 10 this is LossOfPrecision, is this correct? check vs cocoa.
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"-1e-128"];
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"10"];
try {
dcmn3 = [dcmn1 decimalNumberByDividingBy:dcmn2];
[self fail:"decimalNumberByDividingBy: TEX3 - should have thrown underflow error "];
}
catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"-1e-128"];
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"0"];
try {
dcmn3 = [dcmn1 decimalNumberByDividingBy:dcmn2];
[self fail:"decimalNumberByDividingBy: TEX3 - should have thrown DIV0 error "];
}
catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
}
*/
- (void)testMutliply
{
var dcmn1 = [CPDecimalNumber decimalNumberWithString:@"17"];
var dcmn2 = [CPDecimalNumber decimalNumberWithString:@"512"];
var dcmn3 = [dcmn1 decimalNumberByMultiplyingBy:dcmn2];
var d1 = [dcmn3 decimalValue];
[self assert:0 equals:d1._exponent message:"decimalNumberByMultiplyingBy: - exponent"];
[self assert:[8,7,0,4] equals:d1._mantissa message:"decimalNumberByMultiplyingBy: - mantissa"];
[self assert:NO equals:d1._isNegative message:"decimalNumberByMultiplyingBy: - sign"];
[self assert:NO equals:d1._isNaN message:"decimalNumberByMultiplyingBy: - NaN is incorrectly set"];
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"0.0001"];
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"1024"];
dcmn3 = [dcmn1 decimalNumberByMultiplyingBy:dcmn2 withBehavior:_dcmnhWithExactness];
d1 = [dcmn3 decimalValue];
[self assert:-4 equals:d1._exponent message:"decimalNumberByMultiplyingBy:withBehavior: - exponent"];
[self assert:[1,0,2,4] equals:d1._mantissa message:"decimalNumberByMultiplyingBy:withBehavior: - mantissa"];
[self assert:NO equals:d1._isNegative message:"decimalNumberByMultiplyingBy:withBehavior: - sign"];
[self assert:NO equals:d1._isNaN message:"decimalNumberByMultiplyingBy:withBehavior: - NaN is incorrectly set"];
// Exceptions
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"18302741890374918"];
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"123847190277982223422346878934534"];
try {
dcmn3 = [dcmn1 decimalNumberByMultiplyingBy:dcmn2 withBehavior:_dcmnhWithExactness];
[self fail:"decimalNumberByMultiplyingBy:withBehavior: TEX1 - should have thrown precision error "];
}
catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
try {
dcmn3 = [dcmn1 decimalNumberByMultiplyingBy:dcmn2];
}
catch (e)
{
[self fail:"decimalNumberByMultiplyingBy: TEX2 - should not have thrown precision error "];
}
// overflow
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"23e100"];
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"-13e76"];
try {
dcmn3 = [dcmn1 decimalNumberByMultiplyingBy:dcmn2];
[self fail:"decimalNumberByMultiplyingBy: TEX3 - should have overflowed "];
}
catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
}
- (void)testPower10
{
var dcmn1 = [CPDecimalNumber decimalNumberWithString:@"17"];
var p = 21;
var dcmn3 = [dcmn1 decimalNumberByMultiplyingByPowerOf10:p];
var d1 = [dcmn3 decimalValue];
[self assert:21 equals:d1._exponent message:"decimalNumberByMultiplyingByPowerOf10: - exponent"];
[self assert:[1,7] equals:d1._mantissa message:"decimalNumberByMultiplyingByPowerOf10: - mantissa"];
[self assert:NO equals:d1._isNegative message:"decimalNumberByMultiplyingByPowerOf10: - sign"];
[self assert:NO equals:d1._isNaN message:"decimalNumberByMultiplyingByPowerOf10: - NaN is incorrectly set"];
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"1e121"];
p = -123;
dcmn3 = [dcmn1 decimalNumberByMultiplyingByPowerOf10:p withBehavior:_dcmnhWithExactness];
d1 = [dcmn3 decimalValue];
[self assert:-2 equals:d1._exponent message:"decimalNumberByMultiplyingByPowerOf10:withBehavior: - exponent"];
[self assert:[1] equals:d1._mantissa message:"decimalNumberByMultiplyingByPowerOf10:withBehavior: - mantissa"];
[self assert:NO equals:d1._isNegative message:"decimalNumberByMultiplyingByPowerOf10:withBehavior: - sign"];
[self assert:NO equals:d1._isNaN message:"decimalNumberByMultiplyingByPowerOf10:withBehavior: - NaN is incorrectly set"];
// Exceptions
// overflow
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"1e23"];
p = 127;
try {
dcmn3 = [dcmn1 decimalNumberByMultiplyingByPowerOf10:p];
[self fail:"decimalNumberByMultiplyingByPowerOf10: TEX1 - should have overflowed "];
}
catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
}
- (void)testPower
{
var dcmn1 = [CPDecimalNumber decimalNumberWithString:@"17"];
var p = 10;
var dcmn3 = [dcmn1 decimalNumberByRaisingToPower:p];
var d1 = [dcmn3 decimalValue];
[self assert:0 equals:d1._exponent message:"decimalNumberByRaisingToPower: - exponent"];
[self assert:[2,0,1,5,9,9,3,9,0,0,4,4,9] equals:d1._mantissa message:"decimalNumberByRaisingToPower: - mantissa"];
[self assert:NO equals:d1._isNegative message:"decimalNumberByRaisingToPower: - sign"];
[self assert:NO equals:d1._isNaN message:"decimalNumberByRaisingToPower: - NaN is incorrectly set"];
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"54"];
p = 2;
dcmn3 = [dcmn1 decimalNumberByRaisingToPower:p withBehavior:_dcmnhWithExactness];
d1 = [dcmn3 decimalValue];
[self assert:0 equals:d1._exponent message:"decimalNumberByRaisingToPower:withBehavior: - exponent"];
[self assert:[2,9,1,6] equals:d1._mantissa message:"decimalNumberByRaisingToPower:withBehavior: - mantissa"];
[self assert:NO equals:d1._isNegative message:"decimalNumberByRaisingToPower:withBehavior: - sign"];
[self assert:NO equals:d1._isNaN message:"decimalNumberByRaisingToPower:withBehavior: - NaN is incorrectly set"];
// Exceptions
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"123198771093489073413"];
p = 2;
try {
dcmn3 = [dcmn1 decimalNumberByRaisingToPower:p withBehavior:_dcmnhWithExactness];
[self fail:"decimalNumberByRaisingToPower:withBehavior: TEX1 - should have thrown precision error "];
}
catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
try {
dcmn3 = [dcmn1 decimalNumberByRaisingToPower:p];
}
catch (e)
{
[self fail:"decimalNumberByRaisingToPower: TEX2 - should not have thrown precision error "];
}
// overflow
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"1234"];
p = 123;
try {
dcmn3 = [dcmn1 decimalNumberByRaisingToPower:p];
[self fail:"decimalNumberByRaisingToPower: TEX3 - should have overflowed "];
}
catch (e)
{
if ((e.isa) && [e name] == AssertionFailedError)
throw e;
}
}
- (void)testRounding
{
var h = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:CPRoundDown scale:0 raiseOnExactness:NO raiseOnOverflow:YES raiseOnUnderflow:YES raiseOnDivideByZero:YES];
var dcmn1 = [CPDecimalNumber decimalNumberWithString:@"1.4"];
var dcmn3 = [dcmn1 decimalNumberByRoundingAccordingToBehavior:h];
var d1 = [dcmn3 decimalValue];
[self assert:0 equals:d1._exponent message:"decimalNumberByRoundingAccordingToBehavior: - exponent"];
[self assert:[1] equals:d1._mantissa message:"decimalNumberByRoundingAccordingToBehavior: - mantissa"];
[self assert:NO equals:d1._isNegative message:"decimalNumberByRoundingAccordingToBehavior: - sign"];
[self assert:NO equals:d1._isNaN message:"decimalNumberByRoundingAccordingToBehavior: - NaN is incorrectly set"];
}
- (void)testCompare
{
var dcmn1 = [CPDecimalNumber decimalNumberWithString:@"17"];
var dcmn2 = [CPDecimalNumber decimalNumberWithString:@"512"];
var c = [dcmn1 compare:dcmn2];
[self assert:c equals:CPOrderedAscending message:"compare: - ascending"];
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"1234"];
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"512"];
c = [dcmn1 compare:dcmn2];
[self assert:c equals:CPOrderedDescending message:"compare: - descending"];
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"54e123"];
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"54e123"];
c = [dcmn1 compare:dcmn2];
[self assert:c equals:CPOrderedSame message:"compare: - same"];
}
- (void)testStringValue
{
var dcmn = [CPDecimalNumber decimalNumberWithString:@"512"];
[self assert:"512" equals:[dcmn stringValue] message:"stringValue: - small number"];
dcmn = [CPDecimalNumber decimalNumberWithString:@"10823478917236482"];
[self assert:"10823478917236482" equals:[dcmn stringValue] message:"stringValue: - medium number"];
dcmn = [CPDecimalNumber decimalNumberWithString:@"123456789123456789123456789e15"];
[self assert:"123456789123456789123456789000000000000000" equals:[dcmn stringValue] message:"stringValue: - large number"];
dcmn = [CPDecimalNumber decimalNumberWithString:@"82346.2341144"];
[self assert:"82346.2341144" equals:[dcmn descriptionWithLocale:nil] message:"descriptionWithLocale: - large number"];
}
@end
+645
View File
@@ -0,0 +1,645 @@
/*
* CPDecimalTest.j
* Foundation
*
* Created by Stephen Paul Ierodiaconou
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/CPString.j>
@import <Foundation/CPDecimal.j>
@implementation CPDecimalTest : OJTestCase
- (void)testInitialisers
{
// max digits
[self assertNotNull:CPDecimalMakeWithString(@"9999999999999999999999999999999999") message:"CPDecimalMakeWithString() Tmx1: max digits string"];
[self assertNotNull:CPDecimalMakeWithString(@"-1111111111111111111111111111111111") message:"CPDecimalMakeWithString() Tmx2: negative max digits string"];
// too big digit, round
var dcm = CPDecimalMakeWithString(@"11111111111111111111111111111111111111111");
[self assertNotNull:dcm message:"CPDecimalMakeWithString() Tb1: mantissa rounding string"];
[self assert:3 equals:dcm._exponent message:"CPDecimalMakeWithString() Tb1: exponent"];
[self assert:[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] equals:dcm._mantissa message:"CPDecimalMakeWithString() Tb1: mantissa"];
[self assert:NO equals:dcm._isNegative message:"CPDecimalMakeWithString() Tb1: sign"];
[self assert:NO equals:dcm._isNaN message:"CPDecimalMakeWithString() Tb1: NaN is incorrectly set"];
// format tests
dcm = CPDecimalMakeWithString(@"+0.01e+1");
[self assertNotNull:dcm message:"CPDecimalMakeWithString() Tf1: mantissa rounding string"];
[self assert:-1 equals:dcm._exponent message:"CPDecimalMakeWithString() Tf1: exponent"];
[self assert:[1] equals:dcm._mantissa message:"CPDecimalMakeWithString() Tf1: mantissa"];
[self assert:NO equals:dcm._isNegative message:"CPDecimalMakeWithString() Tf1: sign"];
[self assert:NO equals:dcm._isNaN message:"CPDecimalMakeWithString() Tf1: NaN is incorrectly set"];
dcm = CPDecimalMakeWithString(@"+99e-5");
[self assertNotNull:dcm message:"CPDecimalMakeWithString() Tf2: mantissa rounding string"];
[self assert:-5 equals:dcm._exponent message:"CPDecimalMakeWithString() Tf2: exponent"];
[self assert:[9,9] equals:dcm._mantissa message:"CPDecimalMakeWithString() Tf2: mantissa"];
[self assert:NO equals:dcm._isNegative message:"CPDecimalMakeWithString() Tf2: sign"];
[self assert:NO equals:dcm._isNaN message:"CPDecimalMakeWithString() Tf2: NaN is incorrectly set"];
dcm = CPDecimalMakeWithString(@"-1234.5678e100");
[self assertNotNull:dcm message:"CPDecimalMakeWithString() Tf3: mantissa rounding string"];
[self assert:96 equals:dcm._exponent message:"CPDecimalMakeWithString() Tf3: exponent"];
[self assert:[1,2,3,4,5,6,7,8] equals:dcm._mantissa message:"CPDecimalMakeWithString() Tf3: mantissa"];
[self assert:YES equals:dcm._isNegative message:"CPDecimalMakeWithString() Tf3: sign"];
[self assert:NO equals:dcm._isNaN message:"CPDecimalMakeWithString() Tf3: NaN is incorrectly set"];
dcm = CPDecimalMakeWithString(@"0.00000000000000000000000001");
[self assertNotNull:dcm message:"CPDecimalMakeWithString() Tf4: mantissa rounding string"];
[self assert:-26 equals:dcm._exponent message:"CPDecimalMakeWithString() Tf4: exponent"];
[self assert:[1] equals:dcm._mantissa message:"CPDecimalMakeWithString() Tf4: mantissa"];
[self assert:NO equals:dcm._isNegative message:"CPDecimalMakeWithString() Tf4: sign"];
[self assert:NO equals:dcm._isNaN message:"CPDecimalMakeWithString() Tf4: NaN is incorrectly set"];
dcm = CPDecimalMakeWithString(@"000000000000000000");
[self assertNull:dcm message:"CPDecimalMakeWithString() Tf5: Should be invalid"];
// too large return nil
[self assertNull:CPDecimalMakeWithString(@"111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") message:"CPDecimalMakeWithString() To1: number overflow handling"];
[self assertNull:CPDecimalMakeWithString(@"-1e1000") message:"CPDecimalMakeWithString() To2: exponent overflow not caught"];
[self assertNull:CPDecimalMakeWithString(@"-1e-2342") message:"CPDecimalMakeWithString() To3: exponent underflow not caught"];
// Tests for invalid strings
[self assertNull:CPDecimalMakeWithString(@"abc") message:"CPDecimalMakeWithString() Ti1: catch of invalid number string"];
[self assertNull:CPDecimalMakeWithString(@"123a") message:"CPDecimalMakeWithString() Ti2: catch of invalid number string"];
[self assertNull:CPDecimalMakeWithString(@"12.7e") message:"CPDecimalMakeWithString() Ti3: catch of invalid number string"];
[self assertNull:CPDecimalMakeWithString(@"e") message:"CPDecimalMakeWithString() Ti4: catch of invalid number string"];
[self assertNull:CPDecimalMakeWithString(@"12 ") message:"CPDecimalMakeWithString() Ti5: catch of invalid number string"];
[self assertNull:CPDecimalMakeWithString(@"1 2 3") message:"CPDecimalMakeWithString() Ti6: catch of invalid number string"];
[self assertNull:CPDecimalMakeWithString(@"e10") message:"CPDecimalMakeWithString() Ti7: catch of invalid number string"];
[self assertNull:CPDecimalMakeWithString(@"123ee") message:"CPDecimalMakeWithString() Ti8: catch of invalid number string"];
[self assertNull:CPDecimalMakeWithString(@"0001") message:"CPDecimalMakeWithString() Ti9: catch of invalid number string"];
[self assertNull:CPDecimalMakeWithString(@"-0001") message:"CPDecimalMakeWithString() Ti10: catch of invalid number string"];
[self assertNull:CPDecimalMakeWithString(@".123") message:"CPDecimalMakeWithString() Ti11: catch of invalid number string"];
[self assertNull:CPDecimalMakeWithString(@"-.1") message:"CPDecimalMakeWithString() Ti12: catch of invalid number string"];
//test make with parts
dcm = CPDecimalMakeWithParts(10127658,2);
[self assert:2 equals:dcm._exponent message:"CPDecimalMakeWithParts() Tmp1: exponent"];
[self assert:[1,0,1,2,7,6,5,8] equals:dcm._mantissa message:"CPDecimalMakeWithParts() Tmp1: mantissa"];
[self assert:NO equals:dcm._isNegative message:"CPDecimalMakeWithParts() Tmp1: sign"];
[self assert:NO equals:dcm._isNaN message:"CPDecimalMakeWithParts() Tmp1: NaN is incorrectly set"];
dcm = CPDecimalMakeWithParts(-1000000,0);
[self assert:6 equals:dcm._exponent message:"CPDecimalMakeWithParts() Tmp2: exponent"];
[self assert:[1] equals:dcm._mantissa message:"CPDecimalMakeWithParts() Tmp2: mantissa"];
[self assert:YES equals:dcm._isNegative message:"CPDecimalMakeWithParts() Tmp2: sign"];
[self assert:NO equals:dcm._isNaN message:"CPDecimalMakeWithParts() Tmp2: NaN is incorrectly set"];
[self assertNull:CPDecimalMakeWithParts(1,10000) message:"CPDecimalMakeWithParts() Tmp3: exponent overflow not caught"];
[self assertNull:CPDecimalMakeWithParts(-1,-1000) message:"CPDecimalMakeWithParts() Tmp4: exponent underflow not caught"];
}
- (void)testZeros
{
var dcm = CPDecimalMakeWithString(@"0");
[self assertTrue:CPDecimalIsZero(dcm) message:"CPDecimalMakeWithString(0) and CPDecimalIsZero()"];
dcm = CPDecimalMakeWithString(@"-0.000000000000e-0");
[self assertTrue:CPDecimalIsZero(dcm) message:"CPDecimalMakeWithString(0) and CPDecimalIsZero()"];
dcm = CPDecimalMakeWithParts(0,0);
[self assertTrue:CPDecimalIsZero(dcm) message:"CPDecimalMakeWithParts(0)"];
dcm = CPDecimalMakeZero();
[self assertTrue:CPDecimalIsZero(dcm) message:"CPDecimalMakeZero()"];
}
- (void)testOnes
{
var dcm = CPDecimalMakeOne();
[self assertTrue:CPDecimalIsOne(dcm) message:"CPDecimalMakeOne() and CPDecimalIsOne()"];
dcm = CPDecimalMakeWithString(@"1.00000e1");
[self assertTrue:CPDecimalIsOne(dcm) message:"CPDecimalMakeWithString(1)"];
dcm = CPDecimalMakeWithString(@"0.00100000e-3");
[self assertTrue:CPDecimalIsOne(dcm) message:"CPDecimalMakeWithString(1)"];
[self assert:1 equals:dcm._mantissa[0] message:"CPDecimalMakeWithString(1)"];
}
- (void)testNormalize
{
dcm1 = CPDecimalMakeWithString( @"200" );
dcm2 = CPDecimalMakeWithString( @"2" );
[self assert:0 equals:CPDecimalNormalize(dcm1,dcm2,CPRoundDown) message:"CPDecimalNormalise() Tn1:call" ];
[self assert:[2,0,0] equals:dcm1._mantissa message:"CPDecimalNormalise() Tn1: mantissa"];
[self assert:0 equals:dcm1._exponent message:"CPDecimalNormalise() Tn1: exponent"];
[self assert:[2] equals:dcm2._mantissa message:"CPDecimalNormalise() Tn1: mantissa"];
[self assert:0 equals:dcm2._exponent message:"CPDecimalNormalise() Tn1: exponent"];
dcm1 = CPDecimalMakeWithString( @"0.001" );
dcm2 = CPDecimalMakeWithString( @"123" );
[self assert:0 equals:CPDecimalNormalize(dcm1,dcm2,CPRoundDown) message:"CPDecimalNormalise() Tn2:call" ];
[self assert:[1] equals:dcm1._mantissa message:"CPDecimalNormalise() Tn2: mantissa"];
[self assert:-3 equals:dcm1._exponent message:"CPDecimalNormalise() Tn2: exponent"];
[self assert:[1,2,3,0,0,0] equals:dcm2._mantissa message:"CPDecimalNormalise() Tn2: mantissa"];
[self assert:-3 equals:dcm2._exponent message:"CPDecimalNormalise() Tn2: exponent"];
dcm1 = CPDecimalMakeWithString( @"123" );
dcm2 = CPDecimalMakeWithString( @"0.001" );
[self assert:0 equals:CPDecimalNormalize(dcm1,dcm2,CPRoundDown) message:"CPDecimalNormalise() Tn3:call" ];
[self assert:[1] equals:dcm2._mantissa message:"CPDecimalNormalise() Tn3: mantissa"];
[self assert:-3 equals:dcm2._exponent message:"CPDecimalNormalise() Tn3: exponent"];
[self assert:[1,2,3,0,0,0] equals:dcm1._mantissa message:"CPDecimalNormalise() Tn3: mantissa"];
[self assert:-3 equals:dcm1._exponent message:"CPDecimalNormalise() Tn3: exponent"];
dcm1 = CPDecimalMakeWithString( @"-1e-7" );
dcm2 = CPDecimalMakeWithString( @"-21e-8" );
[self assert:0 equals:CPDecimalNormalize(dcm1,dcm2,CPRoundDown) message:"CPDecimalNormalise() Tn4:call" ];
[self assert:[1,0] equals:dcm1._mantissa message:"CPDecimalNormalise() Tn4: mantissa"];
[self assert:-8 equals:dcm1._exponent message:"CPDecimalNormalise() Tn4: exponent"];
[self assert:[2,1] equals:dcm2._mantissa message:"CPDecimalNormalise() Tn4: mantissa"];
[self assert:-8 equals:dcm2._exponent message:"CPDecimalNormalise() Tn4: exponent"];
// these will result in one number becoming zero.
dcm1 = CPDecimalMakeWithString( @"1e0" );
dcm2 = CPDecimalMakeWithString( @"10000000000000000000000000000000000001e2" );
[self assert:CPCalculationLossOfPrecision equals:CPDecimalNormalize(dcm1,dcm2,CPRoundDown) message:"CPDecimalNormalise() Tnp1:call should ret LossOfPrecision" ];
[self assert:[0] equals:dcm1._mantissa message:"CPDecimalNormalise() Tnp1: mantissa"];
[self assert:0 equals:dcm1._exponent message:"CPDecimalNormalise() Tnp1: exponent"];
[self assert:[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1] equals:dcm2._mantissa message:"CPDecimalNormalise() Tnp1: mantissa"];
[self assert:2 equals:dcm2._exponent message:"CPDecimalNormalise() Tnp1: exponent"];
dcm1 = CPDecimalMakeWithString( @"10000000000000000000000000000000000001e2" );
dcm2 = CPDecimalMakeWithString( @"1e0" );
[self assert:CPCalculationLossOfPrecision equals:CPDecimalNormalize(dcm1,dcm2,CPRoundDown) message:"CPDecimalNormalise() Tnp2:call should ret LossOfPrecision" ];
[self assert:[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1] equals:dcm1._mantissa message:"CPDecimalNormalise() Tnp2: mantissa"];
[self assert:2 equals:dcm1._exponent message:"CPDecimalNormalise() Tnp2:exponent"];
[self assert:[0] equals:dcm2._mantissa message:"CPDecimalNormalise() Tnp2: mantissa"];
[self assert:0 equals:dcm2._exponent message:"CPDecimalNormalise() Tnp2: exponent"];
dcm1 = CPDecimalMakeWithString( @"10000000000000000000000000000000000001e127" );
dcm2 = CPDecimalMakeWithString( @"1e0" );
[self assert:CPCalculationLossOfPrecision equals:CPDecimalNormalize(dcm1,dcm2,CPRoundDown) message:"CPDecimalNormalise() Tnp3:call should ret LossOfPrecision" ];
[self assert:[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1] equals:dcm1._mantissa message:"CPDecimalNormalise() Tnp3: mantissa"];
[self assert:127 equals:dcm1._exponent message:"CPDecimalNormalise() Tnp3: exponent"];
[self assert:[0] equals:dcm2._mantissa message:"CPDecimalNormalise() Tnp3: mantissa"];
[self assert:0 equals:dcm2._exponent message:"CPDecimalNormalise() Tnp3: exponent"];
dcm1 = CPDecimalMakeWithString( @"1e-3" );
dcm2 = CPDecimalMakeWithString( @"1e36" );
[self assert:CPCalculationLossOfPrecision equals:CPDecimalNormalize(dcm1,dcm2,CPRoundDown) message:"CPDecimalNormalise() Tnp4:call should ret LossOfPrecision" ];
[self assert:[0] equals:dcm1._mantissa message:"CPDecimalNormalise() Tnp4: mantissa"];
[self assert:0 equals:dcm1._exponent message:"CPDecimalNormalise() Tnp4: exponent"];
[self assert:[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] equals:dcm2._mantissa message:"CPDecimalNormalise() Tnp4: mantissa"];
[self assert:-1 equals:dcm2._exponent message:"CPDecimalNormalise() Tnp4: exponent"];
}
- (void)testRound
{
var dcm = CPDecimalMakeWithString(@"0.00123");
CPDecimalRound(dcm,dcm,3,CPRoundUp);
[self assert:[2] equals:dcm._mantissa message:"CPDecimalRound() Tr1: mantissa"];
[self assert:-3 equals:dcm._exponent message:"CPDecimalRound() Tr1: exponent"];
// plain
var dcm1 = CPDecimalMakeZero();
dcm = CPDecimalMakeWithString(@"123.456");
CPDecimalRound(dcm1,dcm,0,CPRoundPlain);
[self assert:[1,2,3] equals:dcm1._mantissa message:"CPDecimalRound() Tp1: mantissa"];
[self assert:0 equals:dcm1._exponent message:"CPDecimalRound() Tp1: exponent"];
CPDecimalRound(dcm1,dcm,2,CPRoundPlain);
[self assert:[1,2,3,4,6] equals:dcm1._mantissa message:"CPDecimalRound() Tp2: mantissa"];
[self assert:-2 equals:dcm1._exponent message:"CPDecimalRound() Tp2: exponent"];
CPDecimalRound(dcm1,dcm,-2,CPRoundPlain);
[self assert:[1] equals:dcm1._mantissa message:"CPDecimalRound() Tp3: mantissa"];
[self assert:2 equals:dcm1._exponent message:"CPDecimalRound() Tp3: exponent"];
CPDecimalRound(dcm1,dcm,-4,CPRoundPlain);
[self assert:[0] equals:dcm1._mantissa message:"CPDecimalRound() Tp4: mantissa"];
[self assert:0 equals:dcm1._exponent message:"CPDecimalRound() Tp4: exponent"];
CPDecimalRound(dcm1,dcm,6,CPRoundPlain);
[self assert:[1,2,3,4,5,6] equals:dcm1._mantissa message:"CPDecimalRound() Tp5: mantissa"];
[self assert:-3 equals:dcm1._exponent message:"CPDecimalRound() Tp5: exponent"];
// down
CPDecimalRound(dcm1,dcm,0,CPRoundDown);
[self assert:[1,2,3] equals:dcm1._mantissa message:"CPDecimalRound() Td1: mantissa"];
[self assert:0 equals:dcm1._exponent message:"CPDecimalRound() Td1: exponent"];
CPDecimalRound(dcm1,dcm,2,CPRoundDown);
[self assert:[1,2,3,4,5] equals:dcm1._mantissa message:"CPDecimalRound() Td2: mantissa"];
[self assert:-2 equals:dcm1._exponent message:"CPDecimalRound() Td2: exponent"];
CPDecimalRound(dcm1,dcm,-2,CPRoundDown);
[self assert:[1] equals:dcm1._mantissa message:"CPDecimalRound() Td3: mantissa"];
[self assert:2 equals:dcm1._exponent message:"CPDecimalRound() Td3: exponent"];
CPDecimalRound(dcm1,dcm,-4,CPRoundDown);
[self assert:[0] equals:dcm1._mantissa message:"CPDecimalRound() Td4: mantissa"];
[self assert:0 equals:dcm1._exponent message:"CPDecimalRound() Td4: exponent"];
CPDecimalRound(dcm1,dcm,6,CPRoundDown);
[self assert:[1,2,3,4,5,6] equals:dcm1._mantissa message:"CPDecimalRound() Td5: mantissa"];
[self assert:-3 equals:dcm1._exponent message:"CPDecimalRound() Td5: exponent"];
// up
CPDecimalRound(dcm1,dcm,0,CPRoundUp);
[self assert:[1,2,4] equals:dcm1._mantissa message:"CPDecimalRound() Tu1: mantissa"];
[self assert:0 equals:dcm1._exponent message:"CPDecimalRound() Tu1: exponent"];
CPDecimalRound(dcm1,dcm,2,CPRoundUp);
[self assert:[1,2,3,4,6] equals:dcm1._mantissa message:"CPDecimalRound() Tu2: mantissa"];
[self assert:-2 equals:dcm1._exponent message:"CPDecimalRound() Tu2: exponent"];
CPDecimalRound(dcm1,dcm,-2,CPRoundUp);
[self assert:[2] equals:dcm1._mantissa message:"CPDecimalRound() Tu3: mantissa"];
[self assert:2 equals:dcm1._exponent message:"CPDecimalRound() Tu3: exponent"];
CPDecimalRound(dcm1,dcm,-4,CPRoundUp);
[self assert:[0] equals:dcm1._mantissa message:"CPDecimalRound() Tu4: mantissa"];
[self assert:0 equals:dcm1._exponent message:"CPDecimalRound() Tu4: exponent"];
CPDecimalRound(dcm1,dcm,6,CPRoundUp);
[self assert:[1,2,3,4,5,6] equals:dcm1._mantissa message:"CPDecimalRound() Tu5: mantissa"];
[self assert:-3 equals:dcm1._exponent message:"CPDecimalRound() Tu5: exponent"];
// bankers
CPDecimalRound(dcm1,dcm,0,CPRoundBankers);
[self assert:[1,2,3] equals:dcm1._mantissa message:"CPDecimalRound() Tb1: mantissa"];
[self assert:0 equals:dcm1._exponent message:"CPDecimalRound() Tb1: exponent"];
CPDecimalRound(dcm1,dcm,2,CPRoundBankers);
[self assert:[1,2,3,4,6] equals:dcm1._mantissa message:"CPDecimalRound() Tb2: mantissa"];
[self assert:-2 equals:dcm1._exponent message:"CPDecimalRound() Tb2: exponent"];
CPDecimalRound(dcm1,dcm,-2,CPRoundBankers);
[self assert:[1] equals:dcm1._mantissa message:"CPDecimalRound() Tb3: mantissa"];
[self assert:2 equals:dcm1._exponent message:"CPDecimalRound() Tb3: exponent"];
CPDecimalRound(dcm1,dcm,-4,CPRoundBankers);
[self assert:[0] equals:dcm1._mantissa message:"CPDecimalRound() Tb4: mantissa"];
[self assert:0 equals:dcm1._exponent message:"CPDecimalRound() Tb4: exponent"];
CPDecimalRound(dcm1,dcm,6,CPRoundBankers);
[self assert:[1,2,3,4,5,6] equals:dcm1._mantissa message:"CPDecimalRound() Tb5: mantissa"];
[self assert:-3 equals:dcm1._exponent message:"CPDecimalRound() Tb5: exponent"];
// Noscale
CPDecimalRound(dcm1,dcm,CPDecimalNoScale,CPRoundPlain);
[self assert:[1,2,3,4,5,6] equals:dcm1._mantissa message:"CPDecimalRound() Tns1: mantissa"];
[self assert:-3 equals:dcm1._exponent message:"CPDecimalRound() Tns1: exponent"];
}
- (void)testCompare
{
var dcm1 = CPDecimalMakeWithString(@"75836");
var dcm = CPDecimalMakeWithString(@"75836");
var c = CPDecimalCompare(dcm1,dcm);
[self assert:CPOrderedSame equals:c message:"CPDecimalCompare() Tc1: should be same"];
dcm1 = CPDecimalMakeWithString(@"75836");
dcm = CPDecimalMakeWithString(@"75836e-9");
c = CPDecimalCompare(dcm1,dcm);
[self assert:CPOrderedDescending equals:c message:"CPDecimalCompare() Tc2: should be descending"];
dcm1 = CPDecimalMakeWithString(@"823479");
dcm = CPDecimalMakeWithString(@"7082371231252");
c = CPDecimalCompare(dcm1,dcm);
[self assert:CPOrderedAscending equals:c message:"CPDecimalCompare() Tc3: should be descending"];
dcm1 = CPDecimalMakeWithString(@"0.00000000002");
dcm = CPDecimalMakeWithString(@"-1e-9");
c = CPDecimalCompare(dcm1,dcm);
[self assert:CPOrderedDescending equals:c message:"CPDecimalCompare() Tc4: should be descending"];
dcm1 = CPDecimalMakeWithString(@"-23412345123e12");
dcm = CPDecimalMakeWithString(@"-1e100");
c = CPDecimalCompare(dcm1,dcm);
[self assert:CPOrderedDescending equals:c message:"CPDecimalCompare() Tc4: should be descending"];
}
- (void)testCompact
{
var dcm = CPDecimalMakeZero();
dcm._mantissa = [0,0,0,0,1,0,0,0,0,1];
dcm._exponent = 2;
CPDecimalCompact(dcm);
[self assert:2 equals:dcm._exponent message:"CPDecimalCompact() Tcm1: exponent"];
[self assert:[1,0,0,0,0,1] equals:dcm._mantissa message:"CPDecimalCompact() Tcm1: mantissa"];
dcm = CPDecimalMakeZero();
dcm._mantissa = [0,0,0,0,1,2,0,0,0,0];
dcm._exponent = -4;
dcm._isNegative = YES;
CPDecimalCompact(dcm);
[self assert:0 equals:dcm._exponent message:"CPDecimalCompact() Tcm2: exponent"];
[self assert:[1,2] equals:dcm._mantissa message:"CPDecimalCompact() Tcm2: mantissa"];
[self assert:YES equals:dcm._isNegative message:"CPDecimalCompact() Tcm2: sign"];
dcm = CPDecimalMakeZero();
dcm._mantissa = [1,2,0,0,0,0];
dcm._exponent = -1;
CPDecimalCompact(dcm);
[self assert:3 equals:dcm._exponent message:"CPDecimalCompact() Tcm3: exponent"];
[self assert:[1,2] equals:dcm._mantissa message:"CPDecimalCompact() Tcm3: mantissa"];
[self assert:NO equals:dcm._isNegative message:"CPDecimalCompact() Tcm3: sign"];
[self assert:NO equals:dcm._isNaN message:"CPDecimalCompact() Tcm3: NaN is incorrectly set"];
dcm = CPDecimalMakeZero();
dcm._mantissa = [8,9,0,0,0,0,1,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
dcm._exponent = -20;
dcm._isNegative = YES;
CPDecimalCompact(dcm);
[self assert:-5 equals:dcm._exponent message:"CPDecimalCompact() Tcm4: exponent"];
[self assert:[8,9,0,0,0,0,1,2,3] equals:dcm._mantissa message:"CPDecimalCompact() Tcm4: mantissa"];
[self assert:YES equals:dcm._isNegative message:"CPDecimalCompact() Tcm4: sign"];
[self assert:NO equals:dcm._isNaN message:"CPDecimalCompact() Tcm4: NaN is incorrectly set"];
}
- (void)testAdd
{
// test addition of positives
var d1 = CPDecimalMakeWithString(@"1"),
d2 = CPDecimalMakeWithParts(1, 0),
dcm = CPDecimalMakeZero(),
i = 50;
while (i--)
[self assert:CPDecimalAdd(d1, d1, d2, CPRoundPlain) equals:CPCalculationNoError message:"CPDecimalAdd() Tap1: addition"];
[self assert:0 equals:d1._exponent message:"CPDecimalAdd() Tap1: exponent"];
[self assert:[5,1] equals:d1._mantissa message:"CPDecimalAdd() Tap1: mantissa"];
[self assert:NO equals:d1._isNegative message:"CPDecimalAdd() Tap1: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalAdd() Tap1: NaN is incorrectly set"];
// test addition with negatives
d1 = CPDecimalMakeWithString(@"-1");
d2 = CPDecimalMakeWithString(@"-1e-10");
[self assert:CPCalculationNoError equals:CPDecimalAdd(d1, d1, d2, CPRoundPlain) message:"CPDecimalAdd() Tan1: addition of 2 negatives"];
[self assert:-10 equals:d1._exponent message:"CPDecimalAdd() Tan1: exponent"];
[self assert:[1,0,0,0,0,0,0,0,0,0,1] equals:d1._mantissa message:"CPDecimalAdd() Tan1: mantissa"];
[self assert:YES equals:d1._isNegative message:"CPDecimalAdd() Tan1: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalAdd() Tan1: NaN is incorrectly set"];
d1 = CPDecimalMakeWithString(@"-5");
d2 = CPDecimalMakeWithString(@"11");
[self assert:CPCalculationNoError equals:CPDecimalAdd(d1, d1, d2, CPRoundPlain) message:"CPDecimalAdd() Tan2: addition with negatives"];
[self assert:0 equals:d1._exponent message:"CPDecimalAdd() Tan2: exponent"];
[self assert:[6] equals:d1._mantissa message:"CPDecimalAdd() Tan2: mantissa"];
[self assert:NO equals:d1._isNegative message:"CPDecimalAdd() Tan2: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalAdd() Tan2: NaN is incorrectly set"];
d1 = CPDecimalMakeWithString(@"11");
d2 = CPDecimalMakeWithString(@"-12");
[self assert:CPCalculationNoError equals:CPDecimalAdd(d1, d1, d2, CPRoundPlain) message:"CPDecimalAdd() Tan3: addition with negatives"];
[self assert:0 equals:d1._exponent message:"CPDecimalAdd() Tan3: exponent"];
[self assert:[1] equals:d1._mantissa message:"CPDecimalAdd() Tan3: mantissa"];
[self assert:YES equals:d1._isNegative message:"CPDecimalAdd() Tan3: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalAdd() Tan3: NaN is incorrectly set"];
// FIXME: test mantissa overflow handling - is there a loss of precision?? not according to gnustep - check cocoa!!!! CPCalculationLossOfPrecision?
d1 = CPDecimalMakeWithString(@"12345");
d2 = CPDecimalMakeWithString(@"99999999999999999999999999999999999999");
[self assert:0 equals:CPDecimalAdd(d1, d1, d2, CPRoundPlain) message:"CPDecimalAdd() Tapc1: addition with mantissa overflow and rounding"];
[self assert:1 equals:d1._exponent message:"CPDecimalAdd() Tapc1: exponent"];
[self assert:[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4] equals:d1._mantissa message:"CPDecimalAdd() Tapc1: mantissa"];
[self assert:NO equals:d1._isNegative message:"CPDecimalAdd() Tapc1: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalAdd() Tapc1: NaN is incorrectly set"];
d1 = CPDecimalMakeWithString(@"1e127");
d2 = CPDecimalMakeWithString(@"99999999999999999999999999999999999999");
[self assert:CPDecimalAdd(d1, d1, d2, CPRoundPlain) equals:CPCalculationLossOfPrecision message:"CPDecimalAdd() Tapc2: addition loss of precision"];
// Overflow
d1 = CPDecimalMakeWithString(@"1e127");
d2 = CPDecimalMakeWithString(@"99999999999999999999999999999999999999e127");
[self assert:CPDecimalAdd(d1, d1, d2, CPRoundPlain) equals:CPCalculationOverflow message:"CPDecimalAdd() Tao1: addition loss of precision"];
}
- (void)testSubtract
{
var d1 = CPDecimalMakeZero();
var d2 = CPDecimalMakeWithString(@"0.875");
var d3 = CPDecimalMakeWithString(@"12.67");
[self assert:CPCalculationNoError equals:CPDecimalSubtract(d1,d2,d3,CPRoundPlain) message:"CPDecimalSubtract(): Ts1: Should succeed"];
[self assert:-3 equals:d1._exponent message:"CPDecimalSubtract(): Ts1: exponent"];
[self assert:[1,1,7,9,5] equals:d1._mantissa message:"CPDecimalSubtract(): Ts1: mantissa"];
[self assert:YES equals:d1._isNegative message:"CPDecimalSubtract(): Ts1: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalSubtract(): Ts1: NaN is incorrectly set"];
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"-0.875");
d3 = CPDecimalMakeWithString(@"-12.67");
[self assert:CPCalculationNoError equals:CPDecimalSubtract(d1,d2,d3,CPRoundPlain) message:"CPDecimalSubtract(): Ts2: Should succeed"];
[self assert:-3 equals:d1._exponent message:"CPDecimalSubtract(): Ts2: exponent"];
[self assert:[1,1,7,9,5] equals:d1._mantissa message:"CPDecimalSubtract(): Ts2: mantissa"];
[self assert:NO equals:d1._isNegative message:"CPDecimalSubtract(): Ts2: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalSubtract(): Ts2: NaN is incorrectly set"];
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"-0.875");
d3 = CPDecimalMakeWithString(@"12.67e2");
[self assert:CPCalculationNoError equals:CPDecimalSubtract(d1,d2,d3,CPRoundPlain) message:"CPDecimalSubtract(): Ts3: Should succeed"];
[self assert:-3 equals:d1._exponent message:"CPDecimalSubtract(): Ts3: exponent"];
[self assert:[1,2,6,7,8,7,5] equals:d1._mantissa message:"CPDecimalSubtract(): Ts3: mantissa"];
[self assert:YES equals:d1._isNegative message:"CPDecimalSubtract(): Ts3: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalSubtract(): Ts3: NaN is incorrectly set"];
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"0.875");
d3 = CPDecimalMakeWithString(@"-12.67");
[self assert:CPCalculationNoError equals:CPDecimalSubtract(d1,d2,d3,CPRoundPlain) message:"CPDecimalSubtract(): Ts4: Should succeed"];
[self assert:-3 equals:d1._exponent message:"CPDecimalSubtract(): Ts4: exponent"];
[self assert:[1,3,5,4,5] equals:d1._mantissa message:"CPDecimalSubtract(): Ts4: mantissa"];
[self assert:NO equals:d1._isNegative message:"CPDecimalSubtract(): Ts4: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalSubtract(): Ts4: NaN is incorrectly set"];
// loss of precision
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"1e-128");
d3 = CPDecimalMakeWithString(@"1");
[self assert:CPCalculationLossOfPrecision equals:CPDecimalSubtract(d1,d2,d3,CPRoundPlain) message:"CPDecimalSubtract(): Tsp1: Should throw loss of precision"];
[self assert:0 equals:d1._exponent message:"CPDecimalSubtract(): Tsp1: exponent"];
[self assert:[1] equals:d1._mantissa message:"CPDecimalSubtract(): Tsp1: mantissa"];
[self assert:YES equals:d1._isNegative message:"CPDecimalSubtract(): Tsp1: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalSubtract(): Tsp1: NaN is incorrectly set"];
}
- (void)testDivide
{
var d1 = CPDecimalMakeZero();
var d2 = CPDecimalMakeWithString(@"55e12");
var d3 = CPDecimalMakeWithString(@"-5e20");
[self assert:CPCalculationNoError equals:CPDecimalDivide(d1,d2,d3,CPRoundPlain) message:"CPDecimalDivide(): Td1: Should succeed"];
[self assert:-8 equals:d1._exponent message:"CPDecimalDivide(): Td1: exponent"];
[self assert:[1,1] equals:d1._mantissa message:"CPDecimalDivide(): Td1: mantissa"];
[self assert:YES equals:d1._isNegative message:"CPDecimalDivide(): Td1: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalDivide(): Td1: NaN is incorrectly set"];
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"-1e12");
d3 = CPDecimalMakeWithString(@"-1e6");
[self assert:CPCalculationNoError equals:CPDecimalDivide(d1,d2,d3,CPRoundPlain) message:"CPDecimalDivide(): Td2: Should succeed"];
[self assert:6 equals:d1._exponent message:"CPDecimalDivide(): Td2: exponent"];
[self assert:[1] equals:d1._mantissa message:"CPDecimalDivide(): Td2: mantissa"];
[self assert:NO equals:d1._isNegative message:"CPDecimalDivide(): Td2: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalDivide(): Td2: NaN is incorrectly set"];
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"1");
d3 = CPDecimalMakeWithString(@"0.00001");
[self assert:CPCalculationNoError equals:CPDecimalDivide(d1,d2,d3,CPRoundPlain) message:"CPDecimalDivide(): Td3: Should succeed"];
[self assert:5 equals:d1._exponent message:"CPDecimalDivide(): Td3: exponent"];
[self assert:[1] equals:d1._mantissa message:"CPDecimalDivide(): Td3: mantissa"];
[self assert:NO equals:d1._isNegative message:"CPDecimalDivide(): Td3: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalDivide(): Td3: NaN is incorrectly set"];
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"1");
d3 = CPDecimalMakeWithString(@"3");
[self assert:CPCalculationLossOfPrecision equals:CPDecimalDivide(d1,d2,d3,CPRoundPlain) message:"CPDecimalDivide(): Tdp1: Should Loss of precision"];
[self assert:-38 equals:d1._exponent message:"CPDecimalDivide(): Tdp1: exponent"];
[self assert:[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3] equals:d1._mantissa message:"CPDecimalDivide(): Tdp1: mantissa"];
[self assert:NO equals:d1._isNegative message:"CPDecimalDivide(): Tdp1: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalDivide(): Tdp1: NaN is incorrectly set"];
// Why doesnt Cocoa round the final digit up?
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"-0.875");
d3 = CPDecimalMakeWithString(@"12");
[self assert:CPCalculationLossOfPrecision equals:CPDecimalDivide(d1,d2,d3,CPRoundUp) message:"CPDecimalDivide(): Tdp2: Should Loss of precision"];
[self assert:-39 equals:d1._exponent message:"CPDecimalDivide(): Td2: exponent"];
[self assert:[7,2,9,1,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6] equals:d1._mantissa message:"CPDecimalDivide(): Td2: mantissa"];
[self assert:YES equals:d1._isNegative message:"CPDecimalDivide(): Td2: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalDivide(): Td2: NaN is incorrectly set"];
// div zero
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"-0.875");
d3 = CPDecimalMakeWithString(@"0");
[self assert:CPCalculationDivideByZero equals:CPDecimalDivide(d1,d2,d3,CPRoundUp) message:"CPDecimalDivide(): Tdp2: Should Loss of precision"];
}
- (void)testMultiply
{
var d1 = CPDecimalMakeZero();
var d2 = CPDecimalMakeWithString(@"12");
var d3 = CPDecimalMakeWithString(@"-1441231251321235231");
[self assert:CPCalculationNoError equals:CPDecimalMultiply(d1,d2,d3,CPRoundPlain) message:"CPDecimalMultiply(): Tm1: Should succeed"];
[self assert:0 equals:d1._exponent message:"CPDecimalMultiply(): Tm1: exponent"];
[self assert:[1,7,2,9,4,7,7,5,0,1,5,8,5,4,8,2,2,7,7,2] equals:d1._mantissa message:"CPDecimalMultiply(): Tm1: mantissa"];
[self assert:YES equals:d1._isNegative message:"CPDecimalMultiply(): Tm1: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalMultiply(): Tm1: NaN is incorrectly set"];
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"23e100");
d3 = CPDecimalMakeWithString(@"-13e-76");
[self assert:CPCalculationNoError equals:CPDecimalMultiply(d1,d2,d3,CPRoundPlain) message:"CPDecimalMultiply(): Tm2: Should succeed"];
[self assert:24 equals:d1._exponent message:"CPDecimalMultiply(): Tm2: exponent"];
[self assert:[2,9,9] equals:d1._mantissa message:"CPDecimalMultiply(): Tm2: mantissa"];
[self assert:YES equals:d1._isNegative message:"CPDecimalMultiply(): Tm2: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalMultiply(): Tm2: NaN is incorrectly set"];
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"-0.8888");
d3 = CPDecimalMakeWithString(@"8.88e2");
[self assert:CPCalculationNoError equals:CPDecimalMultiply(d1,d2,d3,CPRoundPlain) message:"CPDecimalMultiply(): Tm3: Should succeed"];
[self assert:-4 equals:d1._exponent message:"CPDecimalMultiply(): Tm3: exponent"];
[self assert:[7,8,9,2,5,4,4] equals:d1._mantissa message:"CPDecimalMultiply(): Tm3: mantissa"];
[self assert:YES equals:d1._isNegative message:"CPDecimalMultiply(): Tm3: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalMultiply(): Tm3: NaN is incorrectly set"];
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"-1e111");
d3 = CPDecimalMakeWithString(@"-1e-120");
[self assert:CPCalculationNoError equals:CPDecimalMultiply(d1,d2,d3,CPRoundPlain) message:"CPDecimalMultiply(): Tm4: Should succeed"];
[self assert:-9 equals:d1._exponent message:"CPDecimalMultiply(): Tm4: exponent"];
[self assert:[1] equals:d1._mantissa message:"CPDecimalMultiply(): Tm4: mantissa"];
[self assert:NO equals:d1._isNegative message:"CPDecimalMultiply(): Tm4: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalMultiply(): Tm4: NaN is incorrectly set"];
// loss of precision
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"212341251123892374823742638472481124");
d3 = CPDecimalMakeWithString(@"127889465810478913");
[self assert:CPCalculationLossOfPrecision equals:CPDecimalMultiply(d1,d2,d3,CPRoundDown) message:"CPDecimalMultiply(): Tmp1: Should throw loss of precision"];
[self assert:15 equals:d1._exponent message:"CPDecimalMultiply(): Tmp1: exponent"]; // 69 diff
[self assert:[2,7,1,5,6,2,0,9,1,7,5,7,6,3,3,5,0,9,2,9,7,4,0,2,3,5,6,0,8,0,6,9,5,2,8,3,9,1] equals:d1._mantissa message:"CPDecimalMultiply(): Tmp1: mantissa"];
[self assert:NO equals:d1._isNegative message:"CPDecimalMultiply(): Tmp1: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalMultiply(): Tmp1: NaN is incorrectly set"];
// overflow
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"1e110");
d3 = CPDecimalMakeWithString(@"1e111");
[self assert:CPCalculationOverflow equals:CPDecimalMultiply(d1,d2,d3,CPRoundDown) message:"CPDecimalMultiply(): Tmo1: Should throw overflow"];
[self assert:YES equals:d1._isNaN message:"CPDecimalMultiply(): Tmo1: NaN is incorrectly set"];
}
// Power is unsigned
- (void)testPower
{
var d1 = CPDecimalMakeZero();
var d2 = CPDecimalMakeWithString(@"123");
var d3 = 12;
[self assert:CPCalculationNoError equals:CPDecimalPower(d1,d2,d3,CPRoundPlain) message:"CPDecimalPower(): Tp1: Should succeed"];
[self assert:0 equals:d1._exponent message:"CPDecimalPower(): Tp1: exponent"];
[self assert:[1,1,9,9,1,1,6,3,8,4,8,7,1,6,9,0,6,2,9,7,0,7,2,7,2,1] equals:d1._mantissa message:"CPDecimalPower(): Tp1: mantissa"];
[self assert:NO equals:d1._isNegative message:"CPDecimalPower(): Tp1: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalPower(): Tp1: NaN is incorrectly set"];
//0.00000 13893554059925661274821814636807535200 1 cocoa (39 digits)
//0.00000 13893554059925661274821814636807535204 This , a few off
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"0.875");
d3 = 101;
[self assert:CPCalculationLossOfPrecision equals:CPDecimalPower(d1,d2,d3,CPRoundUp) message:"CPDecimalPower(): Tp2: Should throw Loss of precision"];
[self assert:-43 equals:d1._exponent message:"CPDecimalPower(): Tp2: exponent"];
[self assert:[1,3,8,9,3,5,5,4,0,5,9,9,2,5,6,6,1,2,7,4,8,2,1,8,1,4,6,3,6,8,0,7,5,3,5,2,0,4] equals:d1._mantissa message:"CPDecimalPower(): Tp2: mantissa"];
[self assert:NO equals:d1._isNegative message:"CPDecimalPower(): Tp2: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalPower(): Tp2: NaN is incorrectly set"];
}
- (void)testPower10
{
var d1 = CPDecimalMakeZero();
var d2 = CPDecimalMakeWithString(@"-0.875");
var d3 = 3;
[self assert:CPCalculationNoError equals:CPDecimalMultiplyByPowerOf10(d1,d2,d3,CPRoundPlain) message:"CPDecimalMultiplyByPowerOf10(): Tpp1: Should succeed"];
[self assert:0 equals:d1._exponent message:"CPDecimalMultiplyByPowerOf10(): Tpp1: exponent"];
[self assert:[8,7,5] equals:d1._mantissa message:"CPDecimalMultiplyByPowerOf10(): Tpp1: mantissa"];
[self assert:YES equals:d1._isNegative message:"CPDecimalMultiplyByPowerOf10(): Tpp1: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalMultiplyByPowerOf10(): Tpp1: NaN is incorrectly set"];
d1 = CPDecimalMakeZero();
d2 = CPDecimalMakeWithString(@"1e-120");
d3 = 130;
[self assert:CPCalculationNoError equals:CPDecimalMultiplyByPowerOf10(d1,d2,d3,CPRoundPlain) message:"CPDecimalMultiplyByPowerOf10(): Tpp2: Should succeed"];
[self assert:10 equals:d1._exponent message:"CPDecimalMultiplyByPowerOf10(): Tpp2: exponent"];
[self assert:[1] equals:d1._mantissa message:"CPDecimalMultiplyByPowerOf10(): Tpp2: mantissa"];
[self assert:NO equals:d1._isNegative message:"CPDecimalMultiplyByPowerOf10(): Tpp2: sign"];
[self assert:NO equals:d1._isNaN message:"CPDecimalMultiplyByPowerOf10(): Tpp2: NaN is incorrectly set"];
}
- (void)testString
{
var dcm = CPDecimalMakeWithString(@"0.00123");
[self assert:"0.00123" equals:CPDecimalString(dcm,nil) message:"CPDecimalString() Ts1"];
dcm = CPDecimalMakeWithString(@"2e3");
[self assert:"2000" equals:CPDecimalString(dcm,nil) message:"CPDecimalString() Ts2"];
dcm = CPDecimalMakeWithString(@"0.00876e-5");
[self assert:"0.0000000876" equals:CPDecimalString(dcm,nil) message:"CPDecimalString() Ts3"];
dcm = CPDecimalMakeWithString(@"98.56e10");
[self assert:"985600000000" equals:CPDecimalString(dcm,nil) message:"CPDecimalString() Ts4"];
dcm = CPDecimalMakeWithString(@"-1e-1");
[self assert:"-0.1" equals:CPDecimalString(dcm,nil) message:"CPDecimalString() Ts5"];
dcm = CPDecimalMakeWithString(@"-5e20");
[self assert:"-500000000000000000000" equals:CPDecimalString(dcm,nil) message:"CPDecimalString() Ts6"];
}
@end