--- ibx/branches/udr/client/FBNumeric.pas 2022/01/10 10:13:17 380 +++ ibx/branches/udr/client/FBNumeric.pas 2022/01/15 00:06:22 381 @@ -52,19 +52,44 @@ uses } -function NewNumeric(aValue: AnsiString): IFBNumeric; overload; -function NewNumeric(aValue: double; aScale: integer): IFBNumeric; overload; -function NewNumeric(aValue: TBCD): IFBNumeric; overload; -function NewNumeric(aValue: currency): IFBNumeric; overload; -function NewNumeric(aValue: Int64): IFBNumeric; overload; +function StrToNumeric(aValue: AnsiString): IFBNumeric; +function DoubleToNumeric(aValue: double): IFBNumeric; +function BCDToNumeric(aValue: TBCD): IFBNumeric; +function CurrToNumeric(aValue: currency): IFBNumeric; +function IntToNumeric(aValue: Int64): IFBNumeric; function NumericFromRawValues(aValue: Int64; aScale: integer): IFBNumeric; +function NumericToDouble(aValue: Int64; aScale: integer): double; function TryStrToNumeric(S: Ansistring; out Value: int64; out scale: integer): boolean; -function NumericToDouble(aValue: Int64; aScale: integer): double; function SafeSmallInt(aValue: Int64): Smallint; function SafeInteger(aValue: Int64): integer; +{Numeric Arithmetic} +function NumericAdd(x,y: IFBNumeric): IFBNumeric; overload; {returns x + y} +function NumericSubtract(x,y: IFBNumeric): IFBNumeric; overload; {returns x - y} +function NumericMultiply(x,y: IFBNumeric): IFBNumeric; overload; {returns x * y} +function NumericDivide(x,y: IFBNumeric): IFBNumeric; overload; {returns x / y} +function NumericCompare(x,y: IFBNumeric): integer; overload; {returns -1: x < y; 0: x = y; 1: x > y} +function NegateNumeric(x: IFBNumeric): IFBNumeric; overload; {returns -x} + +{integer operations} +function NumericAdd(x: IFBNumeric; y: int64): IFBNumeric; overload; {returns x + y} +function NumericSubtract(x: IFBNumeric; y: int64): IFBNumeric; overload; {returns x - y} +function NumericSubtract(x: int64; y: IFBNumeric): IFBNumeric; overload; {returns x - y} +function NumericMultiply(x: IFBNumeric; y: int64): IFBNumeric; overload; {returns x * y} +function NumericDivide(x: IFBNumeric; y: int64): IFBNumeric; overload; {returns x / y} +function NumericDivide(x: int64; y: IFBNumeric): IFBNumeric; overload; {returns x / y} +function NumericCompare(x: IFBNumeric; y: int64): integer; overload; {returns -1: x < y; 0: x = y; 1: x > y} + +{floating point operations} +function NumericAdd(x: IFBNumeric; y: double): IFBNumeric; overload; {returns x + y} +function NumericSubtract(x: IFBNumeric; y: double): IFBNumeric; overload; {returns x - y} +function NumericSubtract(x: double; y: IFBNumeric): IFBNumeric; overload; {returns x - y} +function NumericMultiply(x: IFBNumeric; y: double): IFBNumeric; overload; {returns x * y} +function NumericDivide(x: IFBNumeric; y: double): IFBNumeric; overload; {returns x div y} +function NumericDivide(x: double; y: IFBNumeric): IFBNumeric; overload; {returns x div y} +function NumericCompare(x: IFBNumeric; y: double): integer; overload; {returns -1: x < y; 0: x = y; 1: x > y} implementation @@ -80,19 +105,18 @@ type private FValue: Int64; FScale: integer; -// function AdjustScaleFromCurrency(Value: Currency; aScale: Integer): Int64; public - constructor Create(aValue: Int64; aScale: integer); overload; - constructor Create(aValue: Int64); overload; - constructor Create(aValue: AnsiString); overload; - constructor Create(aValue: double; aScale: integer); overload; - constructor Create(aValue: Currency); overload; - constructor Create(aValue: TBCD); overload; + constructor Create(aValue: Int64; aScale: integer); + constructor CreateFromInt(aValue: Int64); + constructor CreateFromStr(aValue: AnsiString); + constructor CreateFromDouble(aValue: double); + constructor CreateFromCurr(aValue: Currency); + constructor CreateFromBCD(aValue: TBCD); public {IFBNumeric} function getRawValue: Int64; function getScale: integer; - function clone(aNewScale: integer): IFBNumeric; + function AdjustScaleTo(aNewScale: integer): IFBNumeric; function getAsString: AnsiString; function getAsDouble: double; function getAsCurrency: Currency; @@ -102,29 +126,29 @@ type function getAsSmallInt: SmallInt; {scaled - may be truncated} end; -function NewNumeric(aValue: AnsiString): IFBNumeric; +function StrToNumeric(aValue: AnsiString): IFBNumeric; begin - Result := TFBNumeric.Create(aValue); + Result := TFBNumeric.CreateFromStr(aValue); end; -function NewNumeric(aValue: double; aScale: integer): IFBNumeric; +function DoubleToNumeric(aValue: double): IFBNumeric; begin - Result := TFBNumeric.Create(aValue,aScale); + Result := TFBNumeric.CreateFromDouble(aValue); end; -function NewNumeric(aValue: TBCD): IFBNumeric; +function BCDToNumeric(aValue: TBCD): IFBNumeric; begin - Result := TFBNumeric.Create(aValue); + Result := TFBNumeric.CreateFromBCD(aValue); end; -function NewNumeric(aValue: currency): IFBNumeric; +function CurrToNumeric(aValue: currency): IFBNumeric; begin - Result := TFBNumeric.Create(aValue); + Result := TFBNumeric.CreateFromCurr(aValue); end; -function NewNumeric(aValue: Int64): IFBNumeric; +function IntToNumeric(aValue: Int64): IFBNumeric; begin - Result := TFBNumeric.Create(aValue); + Result := TFBNumeric.CreateFromINT(aValue); end; function NumericFromRawValues(aValue: Int64; aScale: integer): IFBNumeric; @@ -229,32 +253,150 @@ begin Result := aValue; end; -{ TFBNumeric } +{AdjustScale returns a raw int64 value derived from x but with aNewScale} -(*function TFBNumeric.AdjustScaleFromCurrency(Value: Currency; aScale: Integer - ): Int64; -var - Scaling : Int64; - i : Integer; -begin - Result := 0; - Scaling := 1; - if aScale < 0 then - begin - for i := -1 downto aScale do - Scaling := Scaling * 10; - result := trunc(Value * Scaling); - end +function AdjustScale(x: IFBNumeric; aNewScale: integer): int64; +var rValue: double; +begin + rValue := x.getrawValue; + Result := Round(rValue * IntPower(10,x.getScale-aNewScale)); +end; + +function CompareInt(a,b: integer): integer; +begin + if a < b then + Result := -1 else - if aScale > 0 then - begin - for i := 1 to aScale do - Scaling := Scaling * 10; - result := trunc(Value / Scaling); - end + if a = b then + Result := 0 + else + Result := 1; +end; + +function NumericAdd(x, y: IFBNumeric): IFBNumeric; +begin + case CompareInt(x.getScale,y.getScale) of + 0: + Result := NumericFromRawValues(x.getRawValue + y.getRawValue,x.getScale); + 1: + Result := NumericFromRawValues(AdjustScale(x,y.getscale) + y.getRawValue,y.getScale); else - result := trunc(Value); -end;*) + Result := NumericFromRawValues(AdjustScale(y,x.getscale) + x.getRawValue,x.getScale); + end; +end; + +function NumericSubtract(x, y: IFBNumeric): IFBNumeric; +begin + case CompareInt(x.getScale,y.getScale) of + 0: + Result := NumericFromRawValues(x.getRawValue - y.getRawValue,x.getScale); + 1: + Result := NumericFromRawValues(AdjustScale(x,y.getscale) - y.getRawValue,y.getScale); + else + Result := NumericFromRawValues(AdjustScale(y,x.getscale) - x.getRawValue,x.getScale); + end; +end; + +function NumericMultiply(x, y: IFBNumeric): IFBNumeric; +begin + Result := NumericFromRawValues(x.getRawValue * y.getRawValue,x.getScale+y.getScale); +end; + +function NumericDivide(x, y: IFBNumeric): IFBNumeric; +var z: double; +begin + {Compute actual value as a double} + z := (x.getRawValue / y.getRawValue) * IntPower(10, x.getScale - y.getScale); + {Return numeric at original no. of decimal places of numerator} + Result := DoubleTONumeric(z).AdjustScaleTo(x.getScale); +end; + +function NumericCompare(x, y: IFBNumeric): integer; +begin + case CompareInt(x.getScale,y.getScale) of + 0: + Result := CompareInt(x.getRawValue,y.getRawValue); + 1: + Result := CompareInt(AdjustScale(x,y.getscale),y.getRawValue); + else + Result := CompareInt(x.getRawValue,AdjustScale(y,x.getscale)); + end; +end; + +function NegateNumeric(x: IFBNumeric): IFBNumeric; +begin + Result := NumericFromRawValues(-x.getRawValue,x.getScale); +end; + +function NumericAdd(x: IFBNumeric; y: int64): IFBNumeric; +begin + Result := NumericAdd(x,IntToNumeric(y)); +end; + +function NumericSubtract(x: IFBNumeric; y: int64): IFBNumeric; +begin + Result := NumericSubtract(x,IntToNumeric(y)); +end; + +function NumericSubtract(x: int64; y: IFBNumeric): IFBNumeric; +begin + Result := NumericSubtract(IntToNumeric(x),y); +end; + +function NumericMultiply(x: IFBNumeric; y: int64): IFBNumeric; +begin + Result := NumericMultiply(x,IntToNumeric(y)); +end; + +function NumericDivide(x: IFBNumeric; y: int64): IFBNumeric; +begin + Result := NumericDivide(x,IntToNumeric(y)); +end; + +function NumericDivide(x: int64; y: IFBNumeric): IFBNumeric; +begin + Result := NumericDivide(IntToNumeric(x),y); +end; + +function NumericCompare(x: IFBNumeric; y: int64): integer; +begin + Result := NumericCompare(x,IntToNumeric(y)); +end; + +function NumericAdd(x: IFBNumeric; y: double): IFBNumeric; +begin + Result := NumericAdd(x,DoubleToNumeric(y)); +end; + +function NumericSubtract(x: IFBNumeric; y: double): IFBNumeric; +begin + Result := NumericSubtract(x,DoubleToNumeric(y)); +end; + +function NumericSubtract(x: double; y: IFBNumeric): IFBNumeric; +begin + Result := NumericSubtract(DoubleToNumeric(x),y); +end; + +function NumericMultiply(x: IFBNumeric; y: double): IFBNumeric; +begin + Result := NumericMultiply(x,DoubleToNumeric(y)); +end; + +function NumericDivide(x: IFBNumeric; y: double): IFBNumeric; +begin + Result := NumericDivide(x,DoubleToNumeric(y)); +end; + +function NumericDivide(x: double; y: IFBNumeric): IFBNumeric; +begin + Result := NumericDivide(DoubleToNumeric(x),y); +end; + +function NumericCompare(x: IFBNumeric; y: double): integer; +begin + Result := NumericCompare(x,DoubleToNumeric(y)); +end; constructor TFBNumeric.Create(aValue: Int64; aScale: integer); begin @@ -263,55 +405,49 @@ begin FScale := aScale; end; -constructor TFBNumeric.Create(aValue: Int64); +constructor TFBNumeric.CreateFromInt(aValue: Int64); begin inherited Create; FValue := aValue; FScale := 0; end; -constructor TFBNumeric.Create(aValue: AnsiString); +constructor TFBNumeric.CreateFromStr(aValue: AnsiString); begin inherited Create; if not TryStrToNumeric(aValue,FValue,FScale) then IBError(ibxeInvalidDataConversion,[aValue]); end; -constructor TFBNumeric.Create(aValue: double; aScale: integer); -var - Scaling : Int64; - i : Integer; -begin - inherited Create; - FScale := aScale; - FValue := 0; - Scaling := 1; - if aScale < 0 then +constructor TFBNumeric.CreateFromDouble(aValue: double); + + function WithinLimits(a: double): boolean; begin - for i := -1 downto aScale do - Scaling := Scaling * 10; - FValue := trunc(aValue * Scaling); - end - else - if aScale > 0 then + a := abs(frac(a)); + Result := (a > 0.001) and (a < 0.999); {avoid small rounding errors converting to decimal} + end; + +var aScale: integer; + i: int64; +begin + aScale := 0; + while WithinLimits(AValue) do begin - for i := 1 to aScale do - Scaling := Scaling * 10; - FValue := trunc(aValue / Scaling); - end - else - FValue := trunc(aValue); -// writeln('Adjusted ',Value,' to ',Result); + aValue := aValue * 10; + Inc(aScale); + end; + i := Round(aValue); + Create(i,-aScale); end; -constructor TFBNumeric.Create(aValue: Currency); +constructor TFBNumeric.CreateFromCurr(aValue: Currency); begin inherited Create; Move(aValue,FValue,sizeof(Int64)); FScale := -4; end; -constructor TFBNumeric.Create(aValue: TBCD); +constructor TFBNumeric.CreateFromBCD(aValue: TBCD); var ScaledBCD: TBCD; begin inherited Create; @@ -330,12 +466,12 @@ begin Result := FScale; end; -function TFBNumeric.clone(aNewScale: integer): IFBNumeric; +function TFBNumeric.AdjustScaleTo(aNewScale: integer): IFBNumeric; begin if FScale = aNewScale then Result := TFBNumeric.Create(FValue,FScale) else - Result := TFBNumeric.Create(Round(FValue * IntPower(10,FScale-aNewScale)),aNewScale); + Result := TFBNumeric.Create(AdjustScale(self,aNewScale),aNewScale); end; function TFBNumeric.getAsString: AnsiString; @@ -384,57 +520,13 @@ var value: int64; begin if FScale <> -4 then begin - value := clone(-4).GetRawValue; + value := AdjustScaleTo(-4).GetRawValue; Move(value,Result,sizeof(Currency)); end else Move(FValue,Result,sizeof(Currency)); end; -(*var - Scaling : Int64; - i : Integer; - FractionText, PadText, CurrText: AnsiString; -begin - Result := 0; - Scaling := 1; - PadText := ''; - if FScale > 0 then - begin - for i := 1 to FScale do - Scaling := Scaling * 10; - result := FValue * Scaling; - end - else - if FScale < 0 then - begin - for i := -1 downto FScale do - Scaling := Scaling * 10; - FractionText := IntToStr(abs(FValue mod Scaling)); - for i := Length(FractionText) to -FScale -1 do - PadText := '0' + PadText; - {$IF declared(DefaultFormatSettings)} - with DefaultFormatSettings do - {$ELSE} - {$IF declared(FormatSettings)} - with FormatSettings do - {$IFEND} - {$IFEND} - if FValue < 0 then - CurrText := '-' + IntToStr(Abs(FValue div Scaling)) + DecimalSeparator + PadText + FractionText - else - CurrText := IntToStr(Abs(FValue div Scaling)) + DecimalSeparator + PadText + FractionText; - try - result := StrToCurr(CurrText); - except - on E: Exception do - IBError(ibxeInvalidDataConversion, [nil]); - end; - end - else - result := FValue; -end; *) - function TFBNumeric.getAsBCD: TBCD; begin Result := DoubleToBCD(getAsDouble);