🎄 12/25. What’s behind 0.1+0.2 in Perl 6

Welcome to Day 12/25 of this year’s Perl 6 One-Liner Advent Calendar! Today, we will examine a one-liner that computes a zero.

say 0.1 + 0.2 - 0.3

If you are familiar with programming, you know well that as soon as you start using floating-point arithmetic, you loose precision, and you can face the small errors very quickly.

You might also saw the website, 0.30000000000000004.com that has a long list of different programming languages and how they print a simple expression 0.1 + 0.2. In most cases, you don’t get an exact value of 0.3. And often when you get it, it is actually the result of rounding during the print operation.

Most likely you are familiar enough with Perl 6, and you know that the above behaviour is explained by the fact that Perl 6 uses rational numbers to store floating-point numbers such as 0.1. That’s right, but looking at the grammar, you will see that the journey is a bit longer.

What is called rat_number in the grammar is a number written in angle brackets:

For each of the numbers 0.1, 0.2, and 0.3, the above code takes their integer and fractional parts, prepares the two integers, $parti and $partf, and passes them to the same constructor of a new constant as we saw in the rat_number action, and after that you get a Rat number.

Now we skip some details, and will take a look at another important part with rational numbers that you have to know about them.

In our example, the integer and the fractional part get the following values:

Having the numbers presented as fractions, it is quite easy to make exact calculations, and that’s why we see a pure zero in the output.

Returning to our fractions. If you print numerator and denominator (using the nude method, for example), you will see that the fraction is normalised if possible:

> <1/10>.nude.say(1 10)

> <2/10>.nude.say(1 5)

> 0.2.nude.say(1 5)

> 0.3.nude.say(3 10)

As you see, instead of 2/10 we have 1/5, which represents the same number with the same accuracy. When you use the number, you should not worry about finding the common divider for both fractions, for example:

> (0.1 + 0.2).nude.say(3 10)

I hope there was not too much nudity behind a trivially-looking one-liner today. See you tomorrow with another portion of something interesting with Perl 6!