Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I set the level of precision for Raku's sqrt?

With Perl, one could use bignum to set the level of precision for all operators. As in:

use bignum ( p => -50 );

print sqrt(20);    # 4.47213595499957939281834733746255247088123671922305

With Raku I have no problems with rationals since I can use Rat / FatRat, but I don't know how to use a longer level of precision for sqrt

say 20.sqrt # 4.47213595499958
like image 694
Julio Avatar asked Aug 19 '20 09:08

Julio


People also ask

Does Python have arbitrary precision?

Generally, In low-level languages like C, the precision of integers is limited to 64-bit, but Python implements Arbitrary-precision integers. Since Python 3 all integers are represented as a bignum and these are limited only by the available memory of the host system.


2 Answers

As stated on Elizabeth's answer, sqrt returns a Num type, thus it has limited precision. See Elizabeth's answer for more detail.

For that reason I created a raku class: BigRoot, which uses newton's method and FatRat types to calculate the roots. You may use it like this:

use BigRoot;

# Can change precision level (Default precision is 30)
BigRoot.precision = 50;

my $root2 = BigRoot.newton's-sqrt: 2;
# 1.41421356237309504880168872420969807856967187537695

say $root2.WHAT;
# (FatRat)

# Can use other root numbers
say BigRoot.newton's-root: root => 3, number => 30;
# 3.10723250595385886687766242752238636285490682906742

# Numbers can be Int, Rational and Num:
say BigRoot.newton's-sqrt: 2.123;
# 1.45705181788431944566113502812562734420538186940001

# Can use other rational roots
say BigRoot.newton's-root: root => FatRat.new(2, 3), number => 30;
# 164.31676725154983403709093484024064018582340849939498

# Results are rounded:

BigRoot.precision = 8;
say BigRoot.newton's-sqrt: 2;
# 1.41421356

BigRoot.precision = 7;
say BigRoot.newton's-sqrt: 2;
# 1.4142136

In general it seems to be pretty fast (at least compared to Perl's bigfloat)

Benchmarks:

|---------------------------------------|-------------|------------|
|  sqrt with 10_000 precision digits    |    Raku     |    Perl    |
|---------------------------------------|-------------|------------|
|  20000000000                          |    0.714    |   3.713    |
|---------------------------------------|-------------|------------|
|  200000.1234                          |    1.078    |   4.269    |
|---------------------------------------|-------------|------------|
|  π                                    |    0.879    |   3.677    |
|---------------------------------------|-------------|------------|
|  123.9/12.29                          |    0.871    |   9.667    |
|---------------------------------------|-------------|------------|
|  999999999999999999999999999999999    |    1.208    |   3.937    |
|---------------------------------------|-------------|------------|
|  302187301.3727 / 123.30219380928137  |    1.528    |   7.587    |
|---------------------------------------|-------------|------------|
|  2 + 999999999999 ** 10               |    2.193    |   3.616    |
|---------------------------------------|-------------|------------|
|  91200937373737999999997301.3727 / π  |    1.076    |   7.419    |
|---------------------------------------|-------------|------------|

If want to implement your own sqrt using newton's method, this the basic idea behind it:

sub newtons-sqrt(:$number, :$precision) returns FatRat {
    my FatRat $error = FatRat.new: 1, 10 ** ($precision + 1);
    my FatRat $guess = (sqrt $number).FatRat;
    my FatRat $input = $number.FatRat;
    my FatRat $diff = $input;

    while $diff > $error {
        my FatRat $new-guess = $guess - (($guess ** 2 - $input) / (2 * $guess));
        $diff = abs($new-guess - $guess);
        $guess = $new-guess;
    }

    return $guess.round: FatRat.new: 1, 10 ** $precision;
}
like image 87
Julio Avatar answered Nov 04 '22 21:11

Julio


In Rakudo, sqrt is implemented using the sqrt_n NQP opcode. Which indicates it only supports native nums (because of the _n suffix). Which implies limited precision.

Internally, I'm pretty sure this just maps to the sqrt functionality of one of the underlying math libraries that MoarVM uses.

I guess what we need is an ecosystem module that would export a sqrt function based on Rational arithmetic. That would give you the option to use higher precision sqrt implementations at the expense of performance. Which then in turn, might turn out to be interesting enough to integrate in core.

like image 38
Elizabeth Mattijsen Avatar answered Nov 04 '22 21:11

Elizabeth Mattijsen