Recent

Author Topic: Fractions  (Read 29102 times)

HappyLarry

  • Full Member
  • ***
  • Posts: 144
Fractions
« on: March 22, 2015, 12:41:06 am »
Are there any built-in procedures or functions in Pascal/Lazarus for dealing with fractions (for instance 2/3 + 1/5) without turning the answer into a floating point number?
Use Lazarus and Free Pascal and stand on the shoulders of giants . . . very generous giants. Thank you.

Bart

  • Hero Member
  • *****
  • Posts: 3476
    • Bart en Mariska's Webstek
Re: Fractions
« Reply #1 on: March 22, 2015, 12:51:32 am »
I gues you want:
Code: [Select]
  writeln(2/3 + 1/5);

And the output should be 13/15 (literally)?
You'll have to do this yourself.
Define your own type to hold the fraction, and (if you want) overload the arithmetic operators (+ - / *).

(B.t.w. 2/3 in pascal actually is is float.)

Bart

Bart

  • Hero Member
  • *****
  • Posts: 3476
    • Bart en Mariska's Webstek
Re: Fractions
« Reply #2 on: March 22, 2015, 01:11:23 am »
Code: [Select]
program fractions;

{$mode objfpc}{$H+}

uses
  Classes, sysutils;

type
  TFraction = record
    numerator,
    denominator: Int64;
  end;


function FractionToStr(F: TFraction): String;
begin
  Result := IntToStr(F.numerator) + '/' + IntToStr(F.denominator);
end;

//from http://wiki.lazarus.freepascal.org/Greatest_common_divisor
function GreatestCommonDivisor(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 AddFractions(F1,F2: TFraction): TFraction;
var
  GCD: Int64;
begin
  Result.denominator := F1.denominator * F2.denominator;
  Result.numerator := F1.numerator * F2.denominator + F2.numerator * F1.denominator;
  GCD := GreatestCommonDivisor(Result.numerator, Result.denominator);
  Result.numerator := Result.numerator div GCD;
  Result.denominator := Result.denominator div GCD
end;

function CreateFraction(n, d: Int64): TFraction;
begin
  if (d = 0) then raise EZeroDivide.Create('denominator cannot be 0');
  Result.numerator := n;
  Result.denominator := d;
end;

var
  F1, F2, Res: TFraction;
begin
  F1 := CreateFraction(2,3);
  F2 := CreateFraction(1,5);
  Res := AddFractions(F1, F2);
  writeln(FractionToStr(F1),' + ',FractionToStr(F2),' = ',FractionToStr(Res));
end.

Outputs:
C:\Users\Bart\LazarusProjecten\ConsoleProjecten\fractions>fractions
2/3 + 1/5 = 13/15
Code: [Select]

Bart


typo

  • Hero Member
  • *****
  • Posts: 3051
Re: Fractions
« Reply #3 on: March 22, 2015, 02:11:04 am »
I have a runtime error 103 in this line:

Code: [Select]
writeln(FractionToStr(F1),' + ',FractionToStr(F2),' = ',FractionToStr(Res));

Mike.Cornflake

  • Hero Member
  • *****
  • Posts: 1248
Re: Fractions
« Reply #4 on: March 22, 2015, 02:41:42 am »
Worked here as a console app.  Laz 1.5 (SVN 48027), FPC 3.1.1
Lazarus Trunk/FPC Trunk on Windows [7, 10]
  Have you tried searching this forum or the wiki?:   http://wiki.lazarus.freepascal.org/Alternative_Main_Page
  BOOKS! (Free and otherwise): http://wiki.lazarus.freepascal.org/Pascal_and_Lazarus_Books_and_Magazines

typo

  • Hero Member
  • *****
  • Posts: 3051
Re: Fractions
« Reply #5 on: March 22, 2015, 02:53:43 am »
My error, sorry.

HappyLarry

  • Full Member
  • ***
  • Posts: 144
Re: Fractions
« Reply #6 on: March 22, 2015, 12:06:33 pm »
I have adapted the ucomplex unit  for complex numbers
http://code.google.com/p/fpos/source/browse/rtl/inc/ucomplex.pp?r=c387b381d7a05f9328693cdcf59b0b4f633294e4
(which is very easy to follow) and added some of  Bart's code to create a Fractions unit which lets you use the operators + - * / = as usual and deals with negative fractions.

                                                     
Code: [Select]
Unit Fractions;
// based on the ucomplex unit on complex numbers

{$ifndef VER1_0}
{$INLINE ON}
{$define TEST_INLINE}
{$endif VER1_0}

interface

{$ifndef FPUNONE}
uses Sysutils;

type
    Fraction = record
                     num : integer;
                     den : integer;
               end;

    pfraction = ^fraction;


//Assignment if the operand has no denominator
operator := (z:integer) q : Fraction;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}


{ four operator : +, -, * , /  and comparison = }

//Addition
operator + (q1, q2 : Fraction) q : Fraction;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}

operator + (q1 : Fraction; z : integer) q : Fraction;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}

operator + (z : integer; q2 : Fraction) q : Fraction;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}


//Subtraction
operator - (q1, q2 : Fraction) q : Fraction;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}

operator - (q1 : fraction; z : integer) q : Fraction;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}

operator - (z : integer; q2 : Fraction) q : Fraction;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}


//Multiplication

operator * (q1, q2 : Fraction) q : Fraction;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}

operator * (q1 : Fraction; z : integer) q : Fraction;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}

operator * (z : integer; q2             : Fraction) q : Fraction;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}



//Division
operator / (q1, q2 : Fraction) q : Fraction;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}

operator / (q1 : Fraction; z : integer) q : Fraction;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}

operator / (z : integer; q2 : Fraction) q : Fraction;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}


//Equals or comparison
operator = (q1, q2 : Fraction) b : Boolean;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}

operator = (q1 : Fraction; z : integer) b : Boolean;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}

operator = (z:integer; q2 : Fraction) b : Boolean;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}

operator - (q1 : Fraction) q : Fraction;
    {$ifdef TEST_INLINE}
    inline;
    {$endif TEST_INLINE}


{ functions to do with fractions }
function FractionToStr(q:Fraction) : string;
function CreateFraction(n, d: Int64): Fraction;
function GreatestCommonDivisor(a, b: integer): Integer;
//Simplification to the canonical form
Function Canonicalize(q1:Fraction):Fraction;



implementation

operator := (z : integer) q : Fraction;
  {$ifdef TEST_INLINE}
  inline;
  {$endif TEST_INLINE}

  begin
       q.num := z;
       q.den := 1;
  end;

  { four base operations  +, -, * , / }


//addition
operator + (q1, q2 : Fraction) q : Fraction;
  {$ifdef TEST_INLINE}
  inline;
  {$endif TEST_INLINE}
    { addition : z := z1 + z2 }
  begin
       q.num := q1.num*q2.den + q2.num*q1.den;
       q.den := q1.den * q2.den;
  end;

operator + (q1 : Fraction; z:integer) q : Fraction;
  {$ifdef TEST_INLINE}
  inline;
  {$endif TEST_INLINE}
  begin
       q.num := q1.num + z*q1.den;
       q.den := q1.den;
  end;

operator + (z : integer; q2 : fraction) q : Fraction;
  {$ifdef TEST_INLINE}
  inline;
  {$endif TEST_INLINE}

  begin
       q.num := z*q2.den + q2.num;
       q.den := q2.den;
  end;


//Subtraction
operator - (q1, q2 : Fraction) q : Fraction;
  {$ifdef TEST_INLINE}
  inline;
  {$endif TEST_INLINE}
    { addition : z := z1 + z2 }
  begin
       q.num := q1.num*q2.den - q2.num*q1.den;
       q.den := q1.den * q2.den;
  end;

operator - (q1 : Fraction; z:integer) q : Fraction;
  {$ifdef TEST_INLINE}
  inline;
  {$endif TEST_INLINE}
  begin
       q.num := q1.num - z * q1.den;
       q.den := q1.den;
  end;

operator - (z : integer; q2 : Fraction) q : Fraction;
  {$ifdef TEST_INLINE}
  inline;
  {$endif TEST_INLINE}

  begin
       q.num := z*q2.den - q2.num;
       q.den := q2.den;
  end;


//multiplication

operator * (q1, q2 : Fraction) q : Fraction;
  {$ifdef TEST_INLINE}
  inline;
  {$endif TEST_INLINE}

  begin
       q.num := q1.num * q2.num;
       q.den := q1.den * q2.den;
   end;

operator * (q1 : Fraction; z:integer) q : Fraction;
  {$ifdef TEST_INLINE}
  inline;
  {$endif TEST_INLINE}
  begin
       q.num := q1.num * z;
       q.den := q1.den;
  end;

operator * (z : integer; q2 : Fraction) q : Fraction;
  {$ifdef TEST_INLINE}
  inline;
  {$endif TEST_INLINE}

  begin
       q.num := z * q2.num;
       q.den := q2.den;
  end;


//division

operator / (q1, q2 : Fraction) q : Fraction;
  {$ifdef TEST_INLINE}
  inline;
  {$endif TEST_INLINE}

  begin
       q.num := q1.num * q2.den;
       q.den := q1.den * q2.num;
  end;

operator / (q1 : Fraction; z:integer) q : Fraction;
  {$ifdef TEST_INLINE}
  inline;
  {$endif TEST_INLINE}
  begin
       q.num := q1.num;
       q.den := q1.den * z;
  end;

operator / (z : integer; q2 : Fraction) q : Fraction;
  {$ifdef TEST_INLINE}
  inline;
  {$endif TEST_INLINE}

  begin
       q.num := z * q2.den;
       q.den := q2.num;
  end;


// equals

operator = (q1, q2 : Fraction) b : Boolean;
begin
       b := (q1.num = q2.num) and (q1.den = q2.den);
end;

operator = (q1 : Fraction; z : integer) b : Boolean;
begin
       b := (q1.num = z) and (q1.den = 1);
end;

operator = (z : integer ; q2 : Fraction) b : Boolean;
begin
       b := (q2.num = z) and (q2.den = 1);
end;

//negative fractions

operator - (q1 : Fraction) q: Fraction;
begin
       q.num := q1.num*-1;
       q.den := q1.den;
end;



// function to write out a fractional value
function FractionToStr(q:Fraction) : string;
begin
        result := inttostr(q.num)+'/'+inttostr(q.den);
end;

function CreateFraction(n, d: Int64): Fraction;
var
     q:Fraction;
begin
      if (d = 0) then raise EZeroDivide.Create('denominator cannot be 0');
      if (d < 0) then
      begin
           q.num := n*-1;
           q.den := d*-1;
      end
      else
      begin
           q.num := n;
           q.den := d;
      end;
      Result := q;
end;

function GreatestCommonDivisor(a, b: integer): integer;
var
    temp: Int64;
begin
    while b <> 0 do
    begin
      temp := b;
      b := a mod b;
      a := temp
    end;
    result := a
  end;

Function Canonicalize(q1:Fraction):Fraction;
var
    GCD :integer;
    q2:Fraction;
begin
    GCD := GreatestCommonDivisor(q1.num, q1.den);
    q2.num := q1.num div GCD;
    q2.den := q1.den div GCD;
    Result:=q2;
end;

{$else}
implementation
{$endif FPUNONE}
end.


I have tested it (although it should not be used in programs to send humans to Mars yet).
Code: [Select]
program project2;

uses Fractions;

var
  q1, q2, q3, q4:Fraction;
  q:string;

begin
  writeln('Fraction test');

  q1:=CreateFraction(2,3);
  q2:=CreateFraction(1,5);

  q:=FractionToStr(q1);
  writeln(q);

  q:=FractionToStr(q2);
  writeln(q);

  q3:=q1+q2;
  q:=FractionToStr(q3);
  writeln(q);

  q3:=q1-q2;
  q:=FractionToStr(q3);
  writeln(q);

  q3:=q1*q2;
  q:=FractionToStr(q3);
  writeln(q);

  q3:=q1/q2;
  q:=FractionToStr(q3);
  writeln(q);

  q3:=4;
  q:=FractionToStr(q3);
  writeln(q);

  q3:=CreateFraction(-21,33);
  q3:=Canonicalize(q3);
  q:=FractionToStr(q3);
  writeln(q);

  q4:=CreateFraction(-7,11);

  If q3 = q4  then
  begin
     writeln('yes');
  end
  else
  begin
     writeln('No');
  end;

  If q3 = CreateFraction(-7,11)  then
  begin
     writeln('yes');
  end
  else
  begin
     writeln('No');
  end;

  q4:=CreateFraction(3,-4);
  q:=FractionToStr(q4);
  writeln(q);

  q4:=CreateFraction(-3,-4);
  q:=FractionToStr(q4);
  writeln(q);


  readln;
end.

Here is the output
Code: [Select]
Fraction test
2/3
1/5
13/15
7/15
2/15
10/3
4/1
-7/11
yes
yes
-3/4
3/4

All corrections and improvements gratefully received. Many thanks to Bart for his code and suggestions.
« Last Edit: March 22, 2015, 12:09:48 pm by HappyLarry »
Use Lazarus and Free Pascal and stand on the shoulders of giants . . . very generous giants. Thank you.

wp

  • Hero Member
  • *****
  • Posts: 6137
Re: Fractions
« Reply #7 on: March 22, 2015, 12:20:57 pm »
Are there plans to add fraction support to math? There are number formats in spreadsheet files for fractions which I skipped so far in fpspreadsheet but would like to bring in. The main routine needed here is conversion of floats to fractions.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

Bart

  • Hero Member
  • *****
  • Posts: 3476
    • Bart en Mariska's Webstek
Re: Fractions
« Reply #8 on: March 22, 2015, 12:40:51 pm »
Here's a version with operator overloading, using advanced records.

Code: [Select]
program fractions;

{$mode objfpc}{$H+}
{$MODESWITCH  ADVANCEDRECORDS}
uses
  Classes, sysutils;

type

  { TFraction }

  TFraction = record
    Numerator,
    Denominator: Int64;
    procedure Init(ANumerator,ADenominator: Int64);
    procedure Normalize;
    function ToString: String;
    function Resolve: String;
  end;



function GreatestCommonDivisor(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;


operator := (I: Int64) F: TFraction;
begin
  F.Numerator := I;
  F.Denominator := 1;
end;

operator := (S: String) F: TFraction;
var
  N,D,P: Int64;
begin
  P := Pos('/',S);
  if (P > 0) then
  begin
    N := StrToint(Copy(S,1,P-1));
    D := StrToInt(Copy(S,P+1,MaxInt));
    {$PUSH}{$WARNINGS OFF}
    F.Init(N, D);
    {$POP}
  end
  else
  begin
    N := StrToInt(S);
    F := N;
  end;
end;


operator + (L: TFraction; R: TFraction) F: TFraction;
begin
  F.Denominator := L.Denominator * R.Denominator;
  F.Numerator := L.Numerator * R.Denominator + R.Numerator * L.Denominator;
  F.Normalize;
end;

operator + (L: TFraction; R: Int64) F: TFraction;
var
  Temp: TFraction;
begin
  Temp := R;
  F := L + Temp;
end;

operator + (L: Int64; R: TFraction)F : TFraction;
begin
  F := R + L;
end;

operator - (L: TFraction; R: TFraction) F: TFraction;
begin
  R.Numerator := - R.Numerator;
  F := L + R;
end;

operator - (L: TFraction; R: Int64) F: TFraction;
var
  Temp: TFraction;
begin
  Temp := R;
  F := L + Temp;
end;

operator - (L: Int64; R: TFraction) F: TFraction;
begin
  F := R - L;
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;
  Temp.Denominator := R.Numerator;
  F := L * Temp;
end;

operator / (L: TFraction; R: Int64) F: TFraction;
begin
  F := L;
  F.Normalize;
  F.Denominator := F.Denominator * R;
  F.Normalize;
end;

operator / (L: Int64; R: TFraction) F: TFraction;
begin
  F := R / L;
end;


var
  F1, F2, Res: TFraction;

{ TFraction }

procedure TFraction.Init(ANumerator, ADenominator: Int64);
begin
  if (ADenominator = 0) then raise EZeroDivide.Create('TFraction: denominator cannot be 0');
  if (ADenominator < 0) then
  begin
    ANumerator := - ANumerator;
    ADenominator := - ADenominator;
  end;
  Numerator := ANumerator;
  Denominator := ADenominator;
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) + '/' + IntToStr(Denominator);
end;

function TFraction.Resolve: String;
var
  IntPart: Int64;
begin
  Normalize;
  if (Abs(Numerator) > Abs(Denominator)) then
  begin
    IntPart := Numerator div Denominator;
    Numerator := Numerator mod Denominator;
    if (IntPart < 0) then Numerator := -Numerator;
    if (Numerator <> 0) then
      Result := IntToStr(IntPart) + #32 + ToString
    else
      Result := IntToStr(IntPart);
  end
  else
  begin
    Result := ToString;
  end;
end;

begin
  F1.Init(2,3);
  F2.Init(1,5);
  Res := F1 + F2;
  writeln(F1.ToString,' + ',F2.ToString,' = ',Res.ToString);
  Res := F1 - F2;
  writeln(F1.ToString,' - ',F2.ToString,' = ',Res.ToString);
  Res := F1 * F2;
  writeln(F1.ToString,' * ',F2.ToString,' = ',Res.ToString);
  Res := F1 / F2;
  writeln(F1.ToString,' / ',F2.ToString,' = ',Res.ToString);
  Res := '123/456';
  writeln('123/456 -> ',Res.ToString);
  Res := '123';
  writeln('123 -> ',Res.ToString);
  Res := '456/123';
  writeln('456/123: ToString = ',Res.ToString,' Resolve = ',Res.Resolve);
  Res := '456/-123';
  writeln('456/-123: ToString = ',Res.ToString,' Resolve = ',Res.Resolve);
end.

It has an overlaode := operator, so you can do:
Code: [Select]
  Fraction := '123/456';

Example output:
Code: [Select]
C:\Users\Bart\LazarusProjecten\ConsoleProjecten\fractions>fractions
2/3 + 1/5 = 13/15
2/3 - 1/5 = 7/15
2/3 * 1/5 = 2/15
2/3 / 1/5 = 10/3
123/456 -> 123/456
123 -> 123/1
456/123: ToString = 456/123 Resolve = 3 29/41
456/-123: ToString = -456/123 Resolve = -3 29/41
25/5: ToString = 25/5 Resolve = 5

Bart
« Last Edit: March 22, 2015, 12:45:16 pm by Bart »

Bart

  • Hero Member
  • *****
  • Posts: 3476
    • Bart en Mariska's Webstek
Re: Fractions
« Reply #9 on: March 22, 2015, 12:56:09 pm »
...
The main routine needed here is conversion of floats to fractions.

You mean like:
function FloatToFraction(AFloat: Double): TFraction; ?

That is gonna be impossible I guess.
How are you going to test that 2.7182818... is a rational number and not a irrational or transcendental number?

I don't think you were thinking of a trivial solution where you multiply the float by 10^(number of significant digits) and then say that Numerator := Floor(AFloat * 10^(number of significant digits)) and Denominator := 10^(number of significant digits).

Bart

typo

  • Hero Member
  • *****
  • Posts: 3051
Re: Fractions
« Reply #10 on: March 22, 2015, 01:41:55 pm »
Code: [Select]
program FractionsDemo;

{$mode objfpc}{$H+}

uses
  Classes, SysUtils, Fractions;

function CalculateFraction(FirstFraction, AOperator, SecondFraction :string): string;
var
  F1, F2, Res: Fraction;
  S1, S2, S3, S4 :string;
  p :integer;
begin
  p := Pos('/', FirstFraction);
  S1 := Trim(Copy(FirstFraction, 1, p - 1));
  S2 := Trim(Copy(FirstFraction, p + 1, Length(FirstFraction)));
  p := Pos('/', SecondFraction);
  S3 := Copy(SecondFraction, 1, p - 1);
  S4 := Copy(SecondFraction, p + 1, Length(SecondFraction));
  F1 := CreateFraction(StrToInt(S1), StrToInt(S2));
  F2 := CreateFraction(StrToInt(S3), StrToInt(S4));
  case AOperator of
  '+': Res := Canonicalize(F1 + F2);
  '-': Res := Canonicalize(F1 - F2);
  '*': Res := Canonicalize(F1 * F2);
  '/': Res := Canonicalize(F1 / F2);
  end;   
  Result := FractionToStr(Res);
end;

var
  Str1, Str2, StrOp :string;
begin
  WriteLn('Enter first fraction (a/b):');
  ReadLn(Str1);
  WriteLn('Enter operator (+, -, *, /):');
  ReadLn(StrOp);
  WriteLn('Enter second fraction (c/d):');
  ReadLn(Str2);
  WriteLn(CalculateFraction(Str1, StrOp, Str2));
  readln;
end.                 
« Last Edit: March 22, 2015, 01:47:59 pm by typo »

Bart

  • Hero Member
  • *****
  • Posts: 3476
    • Bart en Mariska's Webstek
Re: Fractions
« Reply #11 on: March 22, 2015, 02:07:06 pm »
Fractions unit + demo:
http://svn.code.sf.net/p/flyingsheep/code/trunk/ConsoleProjecten/fractions/

Compared to HappyLarry's code:
  • Supports unitary - operator (F1 := -F2)
  • Supports operators =, <, <=, > and >=
  • Operator = is overloaded for assigning a string (F1 := '123/456')
  • Canonicalize = Normalize (no idea what name is better)
  • Has a ToString method
  • Has a Resolve method (16/3 -> 5 1/3)
  • Slightly faster GreatestCommonDivisor function

Bart
« Last Edit: March 22, 2015, 03:06:57 pm by Bart »

wp

  • Hero Member
  • *****
  • Posts: 6137
Re: Fractions
« Reply #12 on: March 22, 2015, 02:51:31 pm »
Barth, yes, my question was not precise: I mean *formatting* of a float as a fraction, and this does not belong to math but to SysUtils, as an addition to "FormatFloat". I am thinking of a format string such as "# ??/??" which means: "Convert the float to the next fraction with two digits in the denomintator, and split off the integers". This is a rounding operation, such as "FormatFloat('0.00', x)".

There are some Java references to such a function in the internet, but I still have to make sure how they meet the accuracy requirement defined by the count of question marks.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

Bart

  • Hero Member
  • *****
  • Posts: 3476
    • Bart en Mariska's Webstek
Re: Fractions
« Reply #13 on: March 22, 2015, 03:04:34 pm »
Something like (untested)
Code: [Select]
  F := 123.456;
  Digits := 3;
  IntPart := Round(Int(F)) * Round(Exp(10,3));
  FracPart := Round(Frac(F) * Round(Exp(10,3)));
  S := IntToStr(IntPart) + '/' + IntToStr(FracPart);

Bart

wp

  • Hero Member
  • *****
  • Posts: 6137
Re: Fractions
« Reply #14 on: March 22, 2015, 03:43:30 pm »
No. Excel converts the number 123.456 to 15432/125 (for format "???/???") or 123 57/125.

I found this simple algorithm (just varying numerator and denominator until the original number is best approximated) in http://stackoverflow.com/questions/95727/how-to-convert-floats-to-human-readable-fractions

Code: [Select]
procedure FloatAsFraction(AValue: Double; out ANumerator, ADenominator: Integer);
const
  EPSILON = 1E-6;
var
  fraction: Double;
begin
  ANumerator := 1;
  ADenominator := 1;
  fraction := ANumerator / ADenominator;

  while Abs(fraction - AValue) > EPSILON do
  begin
    if fraction < AValue then
      inc(ANumerator)
    else begin
      inc(ADenominator);
      ANumerator := Round(AValue * ADenominator);
    end;
    fraction := ANumerator / ADenominator;
  end;
end;

I think a more efficient way to do it is by means of continued fractions - a good explanation is in http://mathforum.org/library/drmath/view/51886.html, a bit down the page to the posting of Dr Peterson. I'm still searching to find a suitable implementation.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10