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.

In Perl 6, 0.1 + 0.2 is exactly 0.3, and todayâ€™s one-liner prints an exact zero.

Let us dive a bit to Perl 6 internals to see how that works. A few days ago, we saw that the Grammar of Perl 6 (implemented in the Rakudo compiler) has the following fragment that detects numbers:

token numish { [ | 'NaN' >> | <integer> | <dec_number> | <rad_number> | <rat_number> | <complex_number> | 'Inf' >> | $<uinf>='âˆž' | <unum=:No+:Nl> ] }

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:

token rat_number { '<' <bare_rat_number> '>' }

token bare_rat_number {

<?before <.[-âˆ’+0..9<>:boxd]>+? '/'>

<nu=.signed-integer> '/' <de=integer>

}

So if you change the program to:

say <1/10> + <2/10> - <3/10>

then you will immediately be operating rational numbers. Here is a corresponding action that converts numbers in this format:

method rat_number($/) { make $<bare_rat_number>.ast }

method bare_rat_number($/) {

my $nu := $<nu>.ast.compile_time_value;

my $de := $<de>.ast;

my $ast := $*W.add_constant(

'Rat', 'type_new', $nu, $de,:nocache(1));

$ast.node($/);

make $ast;

}

At some point, the abstract syntax tree gets a node containing a constant of the `Rat` type with the `$nu` and `$de` parts as numerator and denominator.

In our example, with numbers written in the form of 0.1, they first pass the `dec_number` token:

token dec_number {

:dba('decimal number')

[

| $<coeff> = [ '.' <frac=.decint> ] <escale>?

| $<coeff> = [<int=.decint> '.' <frac=.decint>]

<escale>?

| $<coeff> = [ <int=.decint> ] <escale>

]

}

The integer and the fractional parts of the number get into the `<int>` and `<frac>` keys of the final Match object. The action method for this grammar token is rather complex. Let me show it to you.

method dec_number($/) { if $<escale> { # wants a Num make $*W.add_numeric_constant: $/, 'Num', ~$/; } else { # wants a Rat my $Int := $*W.find_symbol(['Int']); my $parti; my $partf; # we build up the number in parts if nqp::chars($<int>) { $parti := $<int>.ast; } else { $parti := nqp::box_i(0, $Int); } if nqp::chars($<frac>) { $partf := nqp::radix_I( 10, $<frac>.Str, 0, 4, $Int); $parti := nqp::mul_I($parti, $partf[1], $Int); $parti := nqp::add_I($parti, $partf[0], $Int); $partf := $partf[1]; } else { $partf := nqp::box_i(1, $Int); } my $ast := $*W.add_constant('Rat', 'type_new', $parti, $partf,:nocache(1)); $ast.node($/); make $ast; } }

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:

$parti=1, $partf=10

$parti=2, $partf=10

$parti=3, $partf=10

You can easily see it yourself if you hack on your local copy of Rakudo files.

Alternatively, use the `--target=parse` option in the command line:

$ perl6 --target=parse -e'say 0.1 + 0.2 - 0.3'

A part of the output will contain the data we want to see:

- 0: 0.1 - value: 0.1 - number: 0.1 - numish: 0.1 - dec_number: 0.1 - frac: 1 - int: 0 - coeff: 0.1 - 1: 0.2 - value: 0.2 - number: 0.2 - numish: 0.2 - dec_number: 0.2 - coeff: 0.2 - frac: 2 - int: 0

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!

## 3 thoughts on “ðŸŽ„ 12/25. Whatâ€™s behind 0.1+0.2 in Perl 6”