lazarus-ccr/components/fractions/fractions.pp
lazarus-bart 0f8ef4176e Fractions:
- fix FloatToFraction() if Abs(Value) > 1
- fix bug in Resolve which resulted in altering the value of self


git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4392 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2015-11-18 15:26:44 +00:00

811 lines
23 KiB
ObjectPascal

unit fractions;
{ A unit for doing arithmatics with fractions
Copyright (C) 2015 by Lazarus and FreePascal community
(http://www.lazarus.freepascal.org and http://www.freepascal.org)
Original code by
Bart Broersma (www.flyingsheep.nl)
HappyLarry
Portions copyright by David Peterson and The Math Forum @ Drexel,
(redistributed with the consent of the copyright holder)
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version with the following modification:
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent modules,and
to copy and distribute the resulting executable under terms of your choice,
provided that you also meet, for each linked independent module, the terms
and conditions of the license of that module. An independent module is a
module which is not derived from or based on this library. If you modify
this library, you may extend this exception to your version of the library,
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
for more details.
You should have received a copy of the GNU Library General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
}
{$mode objfpc}{$H+}
{$MODESWITCH ADVANCEDRECORDS}
interface
uses
SysUtils, Math;
type
{ TFraction }
TFraction = record
private
FNumerator: Int64;
FDenominator: Int64;
procedure SetDominator(AValue: Int64);
public
procedure Normalize;
function ToString: String;
function Resolve: String;
function ToFloat: Double;
property Numerator: Int64 read FNumerator write FNumerator;
property Denominator: Int64 read FDenominator write SetDominator;
end;
TFloatToFractionFunc = function(Value, Precision: Double): TFraction;
TGreatestCommonDivisorFunc = function(a, b: Int64): Int64;
function Fraction(ANumerator, ADenominator: Int64): TFraction;
function Fraction(AIntPart, ANumerator, ADenominator: Int64): TFraction;
operator = (F1: TFraction; F2: TFraction) B: Boolean;
operator < (F1: TFraction; F2: TFraction) B: Boolean;
operator > (F1: TFraction; F2: TFraction) B: Boolean;
operator <= (F1: TFraction; F2: TFraction) B: Boolean;
operator >= (F1: TFraction; F2: TFraction) B: Boolean;
operator := (I: Int64) F: TFraction;
operator := (S: String) F: TFraction;
operator + (L: TFraction; R: TFraction) F: TFraction;
operator - (L: TFraction; R: TFraction) F: TFraction;
operator - (L: TFraction) F: TFraction;
operator * (L: TFraction; R: TFraction) F: TFraction;
operator * (L: TFraction; R: Int64) F: TFraction;
operator * (L: Int64; R: TFraction) F: TFraction;
operator / (L: TFraction; R: TFraction) F: TFraction;
operator / (L: TFraction; R: Int64) F: TFraction;
operator ** (L: TFraction; R: Integer) F: TFraction;
function GreatestCommonDivisor_DivisionBased(a, b: Int64): Int64;
function GreatestCommonDivisor_EuclidSubraction(a, b: Int64): Int64;
function CF_FloatToFraction(Value, Precision: Double): TFraction; //based upon Continue Fractios
function MF_FloatToFraction(Value, Precision: Double): TFraction; //Original code by David Peterson and The Math Forum @ Drexel
function StrToFraction(const S: String): TFraction;
function TryStrToFraction(const S: String; out F: TFraction): Boolean;
function StrToFractionDef(const S: String; Def: TFraction): TFraction;
function TryFloatToFraction(Value, Precision: Double; out F: TFraction; AcceptPrecisionError: Boolean = True): Boolean;
function FloatToFractionDef(Value, Precision: Double; Def: TFraction; AcceptPrecisionError: Boolean = True): TFraction;
function Min(a, b: TFraction): TFraction; inline; overload;
function Max(a, b: TFraction): TFraction; inline; overload;
function InRange(const AValue, AMin, AMax: TFraction): Boolean; inline; overload;
function EnsureRange(const AValue, AMin, AMax: TFraction): TFraction; inline; overload;
function Sign(const AValue: TFraction): TValueSign; inline; overload;
function IsZero(const AValue: TFraction): Boolean; overload;
function Abs(const AValue: TFraction): TFraction; overload;
function Floor(D: Double): Int64; overload;
procedure AdjustPrecision(var Precision: Double; Value: Double);
var
FloatToFraction: TFloatToFractionFunc = @MF_FloatTofraction;
GreatestCommonDivisor: TGreatestCommonDivisorFunc = @GreatestCommonDivisor_DivisionBased;
ResourceString
SDenominatorCannotBeZero = 'The denominator of a fraction cannot be 0';
const
FracSymbol: string = '/';
AllowZeroPowerZero: Boolean = False;
SDivisionByZero = 'Division by zero';
SZeroPowerZero = 'Raising 0 to the power 0 is undifined';
SInvalidFraction = '"%s" is not a valid fraction';
SRangeCheckError = 'Range check error';
const
MaxInt64 = High(Int64);
MinInt64 = Low(Int64);
implementation
//Math.Floor returns Integers and does NOT cause RangeCheck or OverFlow errors,
//So Math.Floor(1/9E-11) returns
// -1773790777 [FFFFFFFF964619C7] instead of
// 11111111111 [00000002964619C7] which is the correct Int64 answer
//this causes an infinite loop in CF_FloatToFraction function
function Floor(D: Double): Int64;
begin
Result := Trunc(D);
if Frac(D) < 0 then
Result := Result - 1;
end;
function GreatestCommonDivisor_DivisionBased(a, b: Int64): Int64;
var
temp: Int64;
begin
while b <> 0 do
begin
temp := b;
b := a mod b;
a := temp
end;
result := a
end;
function GreatestCommonDivisor_EuclidSubraction(a, b: Int64): Int64;
//Euclids subtraction method (http://en.wikipedia.org/wiki/Euclidean_algorithm)
//only workswith positive integers
begin
if (a = 0) then Exit(b);
if (b = 0) then Exit(a);
if (a < 0) then a := -a;
if (b < 0) then b := -b;
while not (a = b) do
begin
if (a > b) then
a := a - b
else
b := b - a;
end;
Result := a;
end;
procedure AdjustPrecision(var Precision: Double; Value: Double);
const
MaxPrec = Double(1.0)/MaxInt64;
begin
Precision := Abs(Precision);
if ((Abs(Value) / Precision) > 1E15) then
begin
//writeln('Value / Precision > 1E15');
Precision := Abs(Value) / 1E16;
end;
if (Precision < MaxPrec) then
Precision := MaxPrec;
end;
function InRange64(Value: Double): Boolean; inline;
begin
Result := not ((Value > MaxInt64) or (Value < MinInt64));
end;
procedure CheckRange(Value: Double);
begin
if not InRange64(Value) then
raise ERangeError.Create(SRangeCheckError);
end;
function IsBorderlineValue(Value: Double; out F: TFraction): Boolean;
const
MaxPrec = Double(1.0)/MaxInt64;
ZeroBoundary = MaxPrec / 2;
begin
if (Abs(Value) <= MaxPrec) then
begin
Result := True;
//writeln('Abs(Value) < 1/MaxInt64 [',MaxPrec,']');
if (Abs(Value) < ZeroBoundary) then
begin
//writeln('Abs(Value) < ZeroBoundary [',ZeroBoundary,']');
F.Numerator := 0;
F.Denominator := 1;
end
else
begin
if (Value < 0) then
F.Numerator := -1
else
F.Numerator := 1;
F.Denominator := MaxInt64;
end;
end
else
Result := False;
end;
//uses method of Continued fractions
function CF_FloatToFraction(Value, Precision: Double): TFraction;
var
H1, H2, K1, K2, A, NewA, tmp: Int64;
B, diff, test: Double;
PendingOverFlow, Found: Boolean;
begin
CheckRange(Value);
AdjustPrecision(Precision, Value);
//Borderline cases
if IsBorderlineValue(Value, Result) then
Exit;
H1 := 1;
H2 := 0;
K1 := 0;
K2 := 1;
b := Value;
NewA := Round(Floor(b));
repeat
A := NewA;
tmp := H1;
H1 := (a * H1) + H2;
H2 := tmp;
tmp := K1;
K1 := (a * K1) + K2;
K2 := tmp;
//write('H1=',H1,' K1=',K1,' A=',A);
test := H1 / K1;
//write(' test=',test);
diff := Abs(test - Value);
//write(' diff=',diff);
Found := (diff < Precision);
if not Found then
begin
if (Abs(B-A) < 1E-30) then
B := 1E30 //happens when H1/K2 exactly matches Value
else
B := 1 / (B - A);
//write(' B=',B);
PendingOverFlow := (((Double(B) * H1) + H2) > MaxInt64) or
(((Double(B) * K1) + K2) > MaxInt64) or
(B > MaxInt64);
//writeln(' PendingOverFlow=',PendingOverFlow);
if not PendingOverFlow then
NewA := Round(Floor(B));
{
if PendingOverFlow then
begin
writeln('PendingOverFlow');
writeln('New H1 = ',(Double(NewA) * H1) + H2);
writeln('New K1 = ',(Double(NewA) * K1) + K2);
writeln('B = ',B);
writeln('MaxInt64 = ',Double(MaxInt64));
end;
}
end;
until Found or PendingOverFlow;
Result.Numerator := H1;
Result.Denominator := K1;
end;
{
This implementation of FloatToFraction was originally written by:
David Peterson and The Math Forum @ Drexel
Source: http://mathforum.org/library/drmath/view/51886.html
It was ported to FreePascal by Bart Broersma
Adjustments made:
* Precision is bound by a magnitude of -15 to Value
* Bordeline cases close to zero are handled
* Handle negative values
* Handle negative precision
* Original code dealt with 32-bit integers, it was adjusted for 64-bit integers
The original copyrigh holder has granted me permission to adjust and redistribute
this code under modified LGPL license with kining exception (see COPYING.modifiedLGPL.txt).
When redistributing this code, the comments above MUST also be redistributed with it!
}
function MF_FloatToFraction(Value, Precision: Double): TFraction;
var
IntPart, Count, Num, Denom: Int64;
i: Integer;
TestLow, TestHigh, Test: Double;
L,H: TFraction;
IsNeg: Boolean;
begin // find nearest fraction
//writeln('MF_FloatToFraction:');
//writeln(' Value = ',Value);
//writeln(' Prec = ',Precision);
CheckRange(Value);
AdjustPrecision(Precision, Value);
//Borderline cases
if IsBorderlineValue(Value, Result) then
Exit;
IsNeg := (Value < 0);
Value := Abs(Value);
intPart := Round(Int(Value));
Value := Frac(Value);
L.Numerator := 0;
L.Denominator := 1;
H.Numerator := 1;
H.denominator := 1;
for i := 1 to 100 do //was 100
begin
//writeln(' i = ',i);
testLow := L.Denominator * Value - L.Numerator;
testHigh := H.Numerator - H.Denominator * Value;
//if (testHigh < Precision * H.Denominator) then
if (Abs(H.ToFloat - Value) < Precision) then
begin
//writeln(' (testHigh < Precision * H.Denominator)');
//writeln(' testHigh = ',testHigh);
//writeln(' Precision * H.Denominator = ',Precision * H.Denominator);
break; // high is answer
end;
//if (testLow < Precision * L.Denominator) then
if (Abs(L.ToFloat - Value) < Precision) then
begin // low is answer
//writeln(' (testLow < Precision * L.Denominator)');
//writeln(' testLow = ',testLow);
//writeln(' Precision * L.Denominator = ',Precision * L.Denominator);
H := L;
break;
end;
if Odd(i) then
begin // odd step: add multiple of low to high
//writeln(' Odd step');
test := testHigh / testLow;
count := Round(Int(test)); // "N"
num := (count + 1) * L.Numerator + H.Numerator;
denom := (count + 1) * L.Denominator + H.Denominator;
if ((num > High(Int64) - 1) or // was 8000, 10000
(denom > High(Int64) - 1)) then
//if ((num > $8000) or // was 8000, 10000
//(denom > $10000)) then
begin
//writeln(' ((num > High(Int64) - 1) or (denom > High(Int64) - 1))');
break;
end;
H.Numerator := num - L.Numerator; // new "A"
H.Denominator := denom - L.Denominator;
L.Numerator := num; // new "B"
L.Denominator := denom;
end
else
begin // even step: add multiple of high to low
//writeln(' Even step');
test := testLow / testHigh;
count := Round(Int(test)); // "N"
num := L.Numerator + (count + 1) * H.Numerator;
denom := L.Denominator + (count + 1) * H.Denominator;
if ((num > High(Int64) - 1) or //10000. 10000
(denom > High(Int64) - 1)) then
//if ((num > $10000) or //10000. 10000
//(denom > $10000)) then
begin
//writeln(' ((num > High(Int64) - 1) or (denom > High(Int64) - 1))');
break;
end;
L.Numerator := num - H.Numerator; // new "A"
L.Denominator := denom - H.Denominator;
H.Numerator := num; // new "B"
H.Denominator := denom;
end;
//writeln(' End of loop iteration for i = ',i);
end;
//return Fraction(intPart, 1) + high;
//writeln('MF_FloatToFraction: assigning Result');
//writeln('H = ',H.ToString);
//writeln('IntPart := ',IntPart);
//Avoid call to TFraction.Normalize in Result := H + IntPart
Result := H;
Result.Numerator := Result.Numerator+ (Result.Denominator * IntPart);
if IsNeg then
Result.Numerator := -Result.Numerator;
//writeln('MF_FloatToFraction End.');
end;
function TryFloatToFraction(Value, Precision: Double; out F: TFraction;
AcceptPrecisionError: Boolean): Boolean;
begin
Result := False;
if not InRange64(Value) then Exit;
AdjustPrecision(Precision, Value);
try
F := FloatToFraction(Value, Precision);
Result := AcceptPrecisionError or (Abs(Value - F.ToFloat) <= Precision)
except
Result := False;
end
end;
function FloatToFractionDef(Value, Precision: Double; Def: TFraction;
AcceptPrecisionError: Boolean): TFraction;
begin
if not TryFloatToFraction(Value, Precision, Result, AcceptPrecisionError) then
Result := Def;
end;
function StrToFraction(const S: String): TFraction;
begin
if not TryStrToFraction(S, Result) then raise EConvertError.CreateFmt(SInvalidFraction, [S]);
end;
{
S is either:
A single fraction e.g. 2/3
A single integer e.g. 10
An integer + a fraction, separated by a single space (#32) e.g. 2 1/2 (two and a half)
}
function TryStrToFraction(const S: String; out F: TFraction): Boolean;
type
TFracType = (ftComplex, ftSingleFraction, ftInteger);
var
FracType: TFracType;
SInt, SNum, SDen: String;
IntPart, NumPart, DenPart: Integer;
PSpace, PFrac: SizeInt;
begin
Result := False;
SInt := '';
SNum := '';
SDen := '';
IntPart := 0;
NumPart := 0;
DenPart := 0;
PSpace := Pos(#32, S);
PFrac := Pos(FracSymbol,S);
if (PSpace > 0) and (PFrac > 0) then FracType := ftComplex
else if (PSpace = 0) and (PFrac > 0) then FracType := ftSingleFraction
else FracType := ftInteger;
case FracType of
ftComplex:
begin
//writeln('ftComplex');
SInt := Copy(S, 1, PSpace - 1);
SNum := Copy(S, PSpace + 1, PFrac - PSpace - 1);
SDen := Copy(S, PFrac + Length(FracSymbol), MaxInt);
//no spaces allowed in SNum or SDen
//writeln('SInt = "',sint,'" SNum = "',snum,'" SDen = "',sden,'"');
if (Pos(#32, SNum) > 0) or (Pos(#32, SDen) > 0) then Exit;
if not (TryStrToInt(SInt, IntPart) and TryStrToInt(SNum, NumPart) and TryStrToInt(SDen, DenPart))
or (DenPart = 0) or (NumPart < 0) then
Exit;
if (DenPart < 0) then
begin
DenPart := -DenPart;
NumPart := -NumPart;
end;
if (IntPart = 0) then
begin
F.Numerator := NumPart;
F.Denominator := DenPart;
end
else
begin
if (NumPart < 0) then
begin
NumPart := -NumPart;
IntPart := -IntPart;
end;
if (IntPart > 0) then
F.Numerator := NumPart + Int64(DenPart) * IntPart
else
F.Numerator := -(NumPart - Int64(DenPart) * IntPart);
F.Denominator := DenPart;
end;
end;
ftSingleFraction:
begin
//writeln('ftSingleFraction');
SNum := Copy(S, 1, PFrac - 1);
SDen := Copy(S, PFrac + Length(FracSymbol), MaxInt);
//writeln('SNum = "',snum,'" SDen = "',sden,'"');
//no spaces allowed in SNum or SDen
if (Pos(#32, SNum) > 0) or (Pos(#32, SDen) > 0) then Exit;
if not (TryStrToInt(SNum, NumPart) and TryStrToInt(SDen, DenPart))
or (DenPart = 0) then
Exit;
F.Numerator := NumPart;
F.Denominator := DenPart;
end;
ftInteger:
begin
//writeln('ftInteger');
//no spaces allowed in SInt
if (Pos(#32, SInt) > 0) then Exit;
if not TryStrToInt(S, IntPart) then Exit;
F.Numerator := IntPart;
F.Denominator := 1;
end;
end;//case
Result := True;
end;
function StrToFractionDef(const S: String; Def: TFraction): TFraction;
begin
if not TryStrToFraction(S, Result) then Result := Def;
end;
function Min(a, b: TFraction): TFraction;
begin
if (a < b) then
Result := b
else
Result := b;
end;
function Max(a, b: TFraction): TFraction;
begin
if (a > b) then
Result := a
else
Result := b;
end;
function InRange(const AValue, AMin, AMax: TFraction): Boolean;
begin
Result := (AValue >= AMin) and (AValue <= AMax);
end;
function EnsureRange(const AValue, AMin, AMax: TFraction): TFraction;
begin
If (AValue < AMin) then
Result := AMin
else if (AValue > AMax) then
Result := AMax
else
Result := AValue;
end;
function Sign(const AValue: TFraction): TValueSign;
begin
if (AValue.Denominator < 0) then
begin
AValue.Numerator := -AValue.Numerator;
AValue.Denominator := -AValue.Denominator;
end;
if (AValue.Numerator < 0) then
Result := NegativeValue
else if (AValue.Numerator > 0) then
Result := PositiveValue
else
Result := ZeroValue;
end;
function IsZero(const AValue: TFraction): Boolean;
begin
Result := (AValue.Numerator = 0);
end;
function Abs(const AValue: TFraction): TFraction;
begin
Result := AValue;
Result.Normalize;
if (Result.Numerator < 0) then Result.Numerator := -Result.Numerator;
end;
function Fraction(ANumerator, ADenominator: Int64): TFraction;
begin
Result.Numerator := ANumerator;
//will raise exception if ADenominator = 0
Result.Denominator := ADenominator;
end;
function Fraction(AIntPart, ANumerator, ADenominator: Int64): TFraction;
var
IsNeg: Boolean;
begin
if (ANumerator < 0) then
raise EMathError.CreateFmt(SInvalidFraction,[Format('%d %d/%d',[AIntPart,ANumerator,ADenominator])]);
IsNeg := (AIntPart < 0) xor (ADenominator < 0);
AIntPart := Abs(AIntPart);
ADenominator := Abs(ADenominator);
Result.Numerator := ANumerator + (ADenominator * AIntPart);
if IsNeg then Result.Numerator := - Result.Numerator;
//will raise exception if ADenominator = 0
Result.Denominator := ADenominator;
end;
operator = (F1: TFraction; F2: TFraction) B: Boolean;
begin
F1.Normalize;
F2.Normalize;
B := (F1.Numerator = F2.Numerator) and (F1.Denominator = F2.Denominator);
end;
operator < (F1: TFraction; F2: TFraction) B: Boolean;
begin
F1.Normalize;
F2.Normalize;
B := (F1.Numerator * F2.Denominator) < (F2.Numerator * F1.Denominator);
end;
operator > (F1: TFraction; F2: TFraction) B: Boolean;
begin
F1.Normalize;
F2.Normalize;
B := (F1.Numerator * F2.Denominator) > (F2.Numerator * F1.Denominator);
end;
operator <= (F1: TFraction; F2: TFraction) B: Boolean;
begin
B := (F1 < F2) or (F1 = F2);
end;
operator >= (F1: TFraction; F2: TFraction) B: Boolean;
begin
B := (F1 > F2) or (F1 = F2);
end;
operator := (I: Int64) F: TFraction;
begin
F.Numerator := I;
F.Denominator := 1;
end;
operator := (S: String) F: TFraction;
begin
if not TryStrToFraction(S, F) then
raise EConvertError.CreateFmt(SInvalidFraction, [S]);
end;
operator + (L: TFraction; R: TFraction) F: TFraction;
begin
F.Numerator := L.Numerator * R.Denominator + R.Numerator * L.Denominator;
F.Denominator := L.Denominator * R.Denominator;
F.Normalize;
end;
operator - (L: TFraction; R: TFraction) F: TFraction;
begin
R.Numerator := - R.Numerator;
F := L + R;
end;
operator - (L: TFraction) F: TFraction;
begin
F.Numerator := - L.Numerator;
F.Denominator := L.Denominator;
F.Normalize;
end;
operator * (L: TFraction; R: TFraction) F: TFraction;
begin
L.Normalize;
R.Normalize;
F.Numerator := L.Numerator * R.Numerator;
F.Denominator := L.Denominator * R.Denominator;
F.Normalize;
end;
operator * (L: TFraction; R: Int64) F: TFraction;
begin
F := L;
F.Normalize;
F.Numerator := L.Numerator * R;
F.Normalize;
end;
operator * (L: Int64; R: TFraction) F: TFraction;
begin
F := R * L;
end;
operator / (L: TFraction; R: TFraction) F: TFraction;
var
Temp: TFraction;
begin
Temp.Numerator := R.Denominator;
//this will raise an exception if R = 0
Temp.Denominator := R.Numerator;
F := L * Temp;
end;
operator / (L: TFraction; R: Int64) F: TFraction;
begin
if (R = 0) or (L.Denominator = 0) then raise EZeroDivide.Create(SDivisionByZero);
F := L;
F.Normalize;
F.Denominator := F.Denominator * R;
F.Normalize;
end;
operator ** (L: TFraction; R: Integer) F: TFraction;
var
i: Integer;
begin
L.Normalize;
F := L;
if (R = 0) then
begin
if (L.Numerator = 0) and not AllowZeroPowerZero then raise EMathError.Create(SZeroPowerZero);
F.Numerator := 1;
F.Denominator := 1;
end
else if (R > 0) then
begin
for i := 1 to R-1 do F := F * L
end
else
begin
F := 1 / F;
L := F;
for i := 1 to -(R)-1 do F := F * L ;
end;
end;
procedure TFraction.SetDominator(AValue: Int64);
begin
if (AValue = 0) then raise EZeroDivide.Create(SDivisionByZero);
if (FDenominator = AValue) then Exit;
FDenominator := AValue;
end;
procedure TFraction.Normalize;
var
GCD: Int64;
begin
if (Denominator < 0) then
begin
Numerator := - Numerator;
Denominator := - Denominator;
end;
GCD := GreatestCommonDivisor(Numerator, Denominator);
if (GCD <> 1) then
begin
Numerator := Numerator div GCD;
Denominator := Denominator div GCD
end;
end;
function TFraction.ToString: String;
begin
if (Denominator < 0) then
begin
Numerator := - Numerator;
Denominator := - Denominator;
end;
Result := IntToStr(Numerator) + FracSymbol + IntToStr(Denominator);
end;
function TFraction.Resolve: String;
var
Num, IntPart: Int64;
begin
Normalize;
if (Abs(Numerator) > Abs(Denominator)) then
begin
IntPart := Numerator div Denominator;
Num := Numerator mod Denominator;
if (IntPart < 0) then Num := -Num;
if (Num <> 0) then
Result := IntToStr(IntPart) + #32 + IntToStr(Num) + FracSymbol + IntToStr(Denominator)
else
Result := IntToStr(IntPart);
end
else
begin
Result := ToString;
end;
end;
function TFraction.ToFloat: Double;
begin
Result := Numerator / Denominator;
end;
end.