Perl 6 small stuff #11: Can Perl 6 pass the Numberphile calculator tests?

This article is just for fun. No really: This is at the silly end of the spectrum. Stop reading here if silly’s not your cup of tea.

Recently I’ve been watching the YouTube channel Numberphile and the inimitable Matt Parker’s Calculator Unboxing series. Numberphile usually covers far more serious aspects of mathematics, so this is undoubtedly lighter fare than the Numberphile average.

Pretending to be a representantive of a make-believe society called South Surrey and associated regions calculator appreciation society for professionals and amateurs (SSAARCASFPAA), Matt unboxes various calculators and run them through a few tests. If you haven’t seen any of these unboxings, have a look at the one below to get an impression of what all of this is about. The style is filled equally with sarcasm and love.

What caught my interest is Matt’s standard tests of the calculator’s precision. He has used several variants, but these are the recurring ones:

  1. sin(1e-1²¨ ⁿ) — this feeds the sin function with smaller and smaller numbers — fractions of 1. At some point the sin function will stop working correctly and simply return the same number as you put in; the smaller the input number is when the breakdown occurs, the more precise is the calculator.
  2. The number of digits can the display show. Quite simply, the more the better. No calculator Matt’s tested displayed more than 16.
  3. 2×√2 — the test here is whether multiplying the two will result in 2, or will the floating point calculation not quite round up?
  4. ⅑×9 — a test of how well the calculator handles rational numbers; the ideal answer here is of course 1, although many calculators not unexpectedly ends up with 0.9999...

This gave me an idea: One of the things Perl 6 explicitly says it has going for it, is “maths that works”. I could help but start thinking about how Perl 6 would fare on Matt’s precision tests.

So I implemented the four tests in Perl 6. While I was at it I implemented it in Perl 5 as well, wanting to have something to benchmark the “maths that work” claim against. And you might think I’ve been unfaithful to Camelia, but lately I’ve fooled around with another lady — Julia (there will be more about the similarities and differences between Perl 6 and Julia at a later date). Julia is made to appeal to mathematicians, statisticians, etc., so it seemed fitting to run Julia through the tests as well. As silly as the project may seem, I actually managed to learn something from it.

Here’s the Perl 6 code.

File: precision.p6
#!/usr/bin/env perl6
say "Perl 6.";
say "Testing SIN.";
for (1..240) {
my $a;
if @*ARGS.elems > 0 && @*ARGS[0] eq "bignum" {
$a = FatRat.new(1, 10**$_);
}
else {
$a = 1e-1**$_;
}
my $b = $a.sin;
if ($a == $b) {
say "Gave up at $_, $a == $b";
last;
}
}
say "Testing 'digits'";
my $e = 0;
for (1..50) {
$e = chars(10 ** $_) if chars(10 ** $_) > $e;
}
say "Can display $e digits.";
say "Testing squares";
say "√2×√2 = " ~ (sqrt(2)*sqrt(2));
say "Testing fractions";
say "1/9 x 9 == " ~ ((1/9) * 9);

This script outputs the following:

$ perl6 precision.p6
Perl 6.
Testing SIN.
Gave up at 8, 1.0000000000000005e-08 == 1.0000000000000005e-08
Testing 'digits'
Can display 51 digits.
Testing squares
√2×√2 = 2.0000000000000004
Testing fractions
1/9 x 9 == 1

The output was almost as expected. The sin test didn’t do much better than some of the calculators, it croaks at sin(0.00000001). I haven’t investigated why, but I guess it has something to do with the resolution of 64-bit floating point numbers. We’ll get back to this in a moment.

Meanwhile, the number of “digits” Perl 6 can display is at least 51. Probably more. The integers can be really, really big here, crushing any calculator display.

Given Perl 6’s Rat type, I was not surprised to see that the 1/9 * 9 calculation returned the correct answer 1. The square 2 x square 2, however, returned 2 with a remainder, although an almost insignificant remainder. For all practical purposes Perl 6 is good enough, but in this particular context: Not quite good enough.

Getting back to the sin test and my speculations about limitations of floats, I gave the script an optional parameter, bignum. When the script is called with that parameter, I use a FatRat instead of a Num when testing. Let’s see what happens.

$ perl6 precision.p6 bignum
...
Testing SIN.
Gave up at 8, 0.00000001 == 1e-08
...

Using a FatRat didn’t make a difference. It seems to me that FatRat’s sin function is inherited from Cool; that the FatRat was “dumbed” down to a type with the default resolution. I didn’t manage to program my way around this, but perhaps some of you know a solution?

Then the time came to check good old Perl 5. My version of Perl 5 is 5.18.2 (I use the perl that’s built-in on MacOS 10.14).

File: precision.pl
#!/usr/bin/env perl
use 5.18.0;
say "Perl 5.";
say "Testing SIN";
for (1..240) {
$a = 1e-1**$_;
$b = sin($a);
if ($a == $b) {
say "Gave up at $_, $a == $b";
last;
}
}
say "Testing 'digits'";
my $e = 0;
for (1..50) {
$e = length(10 ** $_) if length(10 ** $_) > $e;
}
say "Can display $e digits.";
say "Testing squares";
say "√2×√2 = " . (sqrt(2)*sqrt(2));
say "Testing fractions";
say "1/9 x 9 == " . ((1/9) * 9);

You probably see that I haven’t explicitly programmed any support for big number types. That is because Perl 5 has that built-in should you want it. We’ll come back to the difference between running this script without and with bignum support. Let’s do without first:

$ perl precision.pl
Perl 5.
Testing SIN
Gave up at 8, 1e-08 == 1e-08
Testing 'digits'
Can display 17 digits.
Testing squares
√2×√2 = 2
Testing fractions
1/9 x 9 == 1

To be honest, the results surprised me. Perl 5 does not promise “maths that works”, but outperformed Perl 6 in one of the tests. It was equally good at sin, but worse when counting number of digits (it outperformed even the best of Matt’s calculators, though). Where it was spot on was on the square 2 x square 2. That returned an even 2, which is, of course, the strict and correct answer. Even 1/9 x 9 returned 1. So Perl 5 shines here.

As mentioned above Perl 5 supports bignum. So I ran the test again with bignum turned on.

$ perl -Mbignum precision.pl
Jos-MacBook-Pro:Desktop oterhals$ perl -Mbignum precision.pl
Perl 5.
Testing SIN
Gave up at 15, 0.000000000000001 == 0.000000000000001
Testing 'digits'
Can display 51 digits.
Testing squares
√2×√2 = 2.0000000000000000000000000000000000000009280765841371888320433735159498932449
Testing fractions
1/9 x 9 == 0.9999999999999999999999999999999999999999

Now Perl 5 beats Perl 6 on some, but looses on one. I didn’t expect that. The sin function became radically more precise. With bignum support Perl 5 can “display” the same number of digits (at least 50). The square 2 x square 2 test didn’t go as well as before, as there was a marginal remainder here too. But the remainder was orders of magnitude smaller than Perl 6’s, so the win still goes to Perl 5. The only test where Perl 5 now loses is on the 1/9 x 9, where it — just as many of the calculators Matt tested — now calculates the answer 0.999999…

But I just didn’t expect Perl 5 to be marginally better than it’s younger sibling.

Enter Julia.

File: precision.jl
#!/usr/bin/env julia
println("Julia 1.0.")
println("Testing SIN.")
for n = 1:240
if size(ARGS)[1] > 0 && ARGS[1] == "bignum"
a = BigFloat(1e-1^n)
else
a = 1e-1^n
end
b = sin(a)
if a == b
println("Gave up at $(n), $(a) == $(b)")
break
end
end
println("Testing 'digits'.")
global e = 0
for i = 1:50
if size(ARGS)[1] > 0 && ARGS[1] == "bignum"
n = parse(BigInt, "10" * repeat("0", i))
else
n = abs(10^i)
end
a = length(string(n))
if a > e
global e = a
end
end
println("Can display $(e) digits.")
println("Testing squares.")
if size(ARGS)[1] > 0 && ARGS[1] == "bignum"
two = BigFloat(2)
else
two = 2
end
println("√2×√2 = " * string(sqrt(two) * sqrt(two)))
println("Testing fractions.")
println("1/9 x 9 == " * string((1/9) * 9))

As you see I programmed support for a big number type here as well. But first I ran the program without:

$ julia precision.jl
Julia 1.0.
Testing SIN.
Gave up at 8, 1.0000000000000005e-8 == 1.0000000000000005e-8
Testing 'digits'.
Can display 19 digits.
Testing squares.
√2×√2 = 2.0000000000000004
Testing fractions.
1/9 x 9 == 1.0

Julia’s on par with Perl6 when it comes to sin, the square test and the fraction test (like Perl 6 Julia has support for rational numbers). Where it loses is on the “digits” test, where it can display 19, two more than Perl 5 but a lot less than Perl 6.

All of that changes with big number support:

$ julia precision.jl bignum
Julia 1.0.
Testing SIN.
Gave up at 39, 1.000000000000002213149443199363729350933583358853618169388822546370221765473868e-39 == 1.000000000000002213149443199363729350933583358853618169388822546370221765473868e-39
Testing 'digits'.
Can display 52 digits.
Testing squares.
√2×√2 = 1.999999999999999999999999999999999999999999999999999999999999999999999999999983
Testing fractions.
1/9 x 9 == 1.0

The precision improves to an almost ridiculous level. On the sin test Julia beats even Perl 5 with bignum support. So much that I don’t bother figuring out how much. Let’s just say that it’s a lot.

For the square 2 x square 2 some may be disappointed to see that the result dropped below 2, but study the result more closely and you’ll see that the precision has become insanely better. Again, I won’t bother figuring out how much. Strangely enough, Perl 5 without bignum is the best of the bunch on this test. But I dare say that Julia using BigFloats is more than precise enough for any application I can imagine.

So… this was meant to be, and became, a collection of silly tests that really don’t mean anything or have any practical application. The only surprise for me was, to be honest, how good Perl 5 was. I guess I’m surprised because Perl 5 is the one that doesn’t promise anything specifically when it comes to maths.

As for Matt Parker, my claim is that he’d be satisfied with the mathematical abilities of all three languages. At the same time I’m almost certain that he’d find them lacking in the ease-of-use departement compared even to the most crippled calculator.

From an isolated Perl 6 point of view, the conclusion must be that Perl 6 is a more than acceptable calculator. But even the most die hard fans would probably have to admit that Perl 6 would make an absolute meaningless unboxing video 😂