← All examples

rationals.raku

Exact rational arithmetic.

Raku's decimal literals are exact rationals, not floating point, so 0.1 + 0.2 is exactly 0.3. This program shows that, then computes exact harmonic numbers and reconstructs π's best rational approximations from its continued fraction.

The program

Edit it and press Run — it executes in your browser.

#!/usr/bin/env raku
# Exact rational arithmetic. In Raku a decimal literal like 0.1 is a `Rat`
# (a numerator/denominator pair), not a floating-point approximation, so
# sums that famously go wrong in binary floating point stay exact here.

say 'Floating-point folklore, done exactly:';
say "  0.1 + 0.2        = {0.1 + 0.2}";          # 0.3, not 0.30000000000000004
say "  0.1 + 0.2 - 0.3  = {0.1 + 0.2 - 0.3}";    # exactly 0
say "  (0.1 + 0.2).nude = {(0.1 + 0.2).nude.join('/')}";  # 3/10 — a real Rat
say '';

# The harmonic numbers H_n = 1 + 1/2 + 1/3 + ... + 1/n are rationals whose
# denominators blow up fast. With a real bignum tower they stay exact.
say 'Harmonic numbers H_n (exact):';
my $h = 0;
for 1 .. 10 -> $n {
    $h += 1 / $n;
    say "  H_$n = {$h.nude.join('/')}" if $n == any(1, 5, 10);
}
say '';

# Continued-fraction convergents of pi: each is the best rational
# approximation of pi for its denominator size. 355/113 is famous for
# being accurate to seven digits.
say 'Rational approximations of pi from its continued fraction:';
my @cf = 3, 7, 15, 1, 292, 1, 1;
for 1 .. @cf -> $len {
    my ($num, $den) = 1, 0;
    for @cf[^$len].reverse -> $a {
        ($num, $den) = $a * $num + $den, $num;
    }
    my $approx = $num / $den;
    say sprintf('  %-9s = %.10f  (error %.2e)',
                "$num/$den", $approx, abs($approx - pi));
}
say '';

# Any rational round-trips exactly through its own continued fraction.
sub to-cf(Rat $r is copy) {
    my @terms;
    loop {
        @terms.push: $r.floor;
        my $frac = $r - $r.floor;
        last if $frac == 0;
        $r = 1 / $frac;
    }
    @terms;
}

my $x = 355/113;
say "Continued fraction of {$x.nude.join('/')} is ", to-cf($x).raku;
Output
Floating-point folklore, done exactly:
  0.1 + 0.2        = 0.3
  0.1 + 0.2 - 0.3  = 0
  (0.1 + 0.2).nude = 3/10

Harmonic numbers H_n (exact):
  H_1 = 1/1
  H_5 = 137/60
  H_10 = 7381/2520

Rational approximations of pi from its continued fraction:
  3/1       = 3.0000000000  (error 1.42e-01)
  22/7      = 3.1428571429  (error 1.26e-03)
  333/106   = 3.1415094340  (error 8.32e-05)
  355/113   = 3.1415929204  (error 2.67e-07)
  103993/33102 = 3.1415926530  (error 5.78e-10)
  104348/33215 = 3.1415926539  (error 3.32e-10)
  208341/66317 = 3.1415926535  (error 1.22e-10)

Continued fraction of 355/113 is [3, 7, 16]

Feature focus: the Rat tower, continued fractions.