From floating point to fraction

J

jon rogers

Hi

I'd like to, from inside my Perl script, go from a floating point number,
like 0.33333333333333333, to a fraction, in this case 1/3. Is that
possible? And if so, how?

/ JR
 
S

Sisyphus

jon rogers said:
Hi

I'd like to, from inside my Perl script, go from a floating point number,
like 0.33333333333333333, to a fraction, in this case 1/3. Is that
possible? And if so, how?

/ JR

Sort of depends on what you mean. Does the numerator always have to be "1" ?
If not, how do you determine in advance the number it should be set to ?

Here's a spur-of-the-moment idea (not necessarily good :)

use warnings;
my $x = "0.33333333333333333";
my $x2 = "0.83733333333333333";

my $y = fractionalise($x, 1);
my $y2 = fractionalise($x2, 1000);
print $y, "\n", $y2, "\n";

sub fractionalise {
my $whole = int($_[1] / $_[0] + 0.5);
return "$_[1]/". $whole;
}
 
W

Wolfgang Fischer

Hi

I'd like to, from inside my Perl script, go from a floating point number,
like 0.33333333333333333, to a fraction, in this case 1/3. Is that
possible? And if so, how?

/ JR

perl -p -e '$_=sprintf("%6f",$_);my($a,$b)=/(\d*)\.(\d*)/;$_=($a*"9"x length($b))+$b."/"."9" x length($b)."\n";'
 
M

Mark Jason Dominus

I'd like to, from inside my Perl script, go from a floating point number,
like 0.33333333333333333, to a fraction, in this case 1/3. Is that
possible? And if so, how?

I think the simplest approach is to use 'continued fractions'. Here's
some code:



sub number_to_fraction {
my $d = shift;
my $c = Convergents->new;
sub {
return unless defined $d;
my $int = int($d);
my $frac = $d - $int;
$d = $frac ? 1/$frac : undef;
$c->input($int);
my ($num, $den) = ($c->nth(-1));
wantarray ? ($num, $den) : $num/$den;
};
}



my $X = shift || 0.3333333333333;
my $fractions = number_to_fraction($X);

for (1..7) {
my ($n, $d) = $fractions->();
print "$n / $d\n";
}



package Convergents;

sub new {
my ($class) = @_;
bless {n=>[0, 1], d=>[1, 0]} => $class;
}

sub input {
my ($self, $n) = @_;
push @{$self->{n}}, $self->{n}[-1] * $n + $self->{n}[-2];
push @{$self->{d}}, $self->{d}[-1] * $n + $self->{d}[-2];
$self;
}

sub nth {
my ($self, $n) = @_;
($self->{n}[$n], $self->{d}[$n])
}




The function 'number_to_fraction' takes a number and returns an object
which generates fractions that are close to that number. The initial
fractions are simple but inaccurate; the later ones are more accurate
but more complicated. You need to arrange your program to decide
which one it wants to use. That's a policy matter that I can't really
help with.

For your example of 0.33333333333333333, the function generates

0 / 1
1 / 3

and then stops, which I think is just what you would want.

For 3.141592654, it generates

3 / 1 = 3.000000000000
22 / 7 = 3.142857142857
333 / 106 = 3.141509433962
355 / 113 = 3.141592920354
104348 / 33215 = 3.141592653921
1148183 / 365478 = 3.141592654004
1252531 / 398693 = 3.141592653997
2400714 / 764171 = 3.141592654000
18057529 / 5747890 = 3.141592654000
38515772 / 12259951 = 3.141592654000

Which as you can see includes the famous 22/7 approximation to pi, as
well as the slightly less famous 355/113 approximation. The 355/113
approximation is very close to the corect answer, which is why the
next value out of the object (10438/33215) has such a large denominator.

In general, the object wil never generate a fraction with a larger
denominator as long as there is a more accurate fraction with a
smaller denominator. That is, it generates the simplest and most
accurate fractions possible.

Hope this helps.

This code is in the public domain; feel free to use it in your
program.
 
I

Ilya Zakharevich

[A complimentary Cc of this posting was sent to
Mark Jason Dominus
I think the simplest approach is to use 'continued fractions'.
You need to arrange your program to decide
which one it wants to use. That's a policy matter that I can't really
help with.

One needs to know the error estimate. When the next fraction is
(within the estimate) of the previous one, one should stop. [Two
consequent fractions are not only both "best" approximations, they
also bracket the initial value between them.]

But if you can use the LLL algorithm, finding the stuff becomes much
more convenient; this is why I did not use PARI's builtin continuous
fraction routines in my sample solution. [It is harder to explain LLL
in 3 minutes, though. ;-(]
For your example of 0.33333333333333333, the function generates

0 / 1
1 / 3

and then stops, which I think is just what you would want.

Nope, it should generate

33333333333333333/100000000000000000

(or some such) too. Your calculations where done with too little
precision...

Hope this helps,
Ilya
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,062
Latest member
OrderKetozenseACV

Latest Threads

Top