Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

atan function #41

Open
josecelano opened this issue Apr 6, 2020 · 8 comments
Open

atan function #41

josecelano opened this issue Apr 6, 2020 · 8 comments

Comments

@josecelano
Copy link

Hi,

I'm trying to find a way to implement atan PHP function with arbitrary precision, but I can't find any implementation.

It seems you can use the Maclaurin Series:

image

But I can not get even the basic PHP atan function precision.

Thanks.

@rtheunissen
Copy link
Contributor

PHP's atan uses native math.c atan, passing a double. We can look at implementing an internal arbitrary precision function for it if there is a standard way to calculate it.

In your observation, is the problem with the series not being precise or ext-decimal not calculating it very well?

@josecelano
Copy link
Author

Hi,

Wow!, that was a very fast reply. Thanks.

I'm trying to build a PHP complex library with arbitrary precision based on this extension.
I was trying to replace atan with the Maclaurin Series using this function:

https://github.com/josecelano/php-complex/blob/master/src/ArbitraryPrecisionComplex/DecimalFunction.php#L15-L25

I do not know yet where is the problem. The difference between using the standard function and the new one is:

-0.78539816339745 <- standard atan function
-0.7878733502677476438131719674 <- Maclaurin Serie with n= 100

Maybe n has to be very big, I tried with 10000 but still, I only get the 2 first decimals numbers equals.

I have found this paper:
https://arxiv.org/pdf/1410.7176.pdf
with an algorithm to implement atan:

image

I will read it although I d not know if it is valid for arbitrary precision.

@josecelano
Copy link
Author

Hi, more feedback. I was able to increase the precision using n=10000000 and it takes a couple of minutes to execute it. The final result is:

-'-0.78539816339745'
+'-0.7853981883974458096158484647'

It's better (7 decimals) but still worse than the standard PHP atan implementation.

I have been trying to find more info.

This page contains a lot of info about the function:
https://mathworld.wolfram.com/InverseTangent.html

I found some c implementations:
https://opensource.apple.com/source/Libm/Libm-315/Source/Intel/atan.c
http://www.netlib.org/fdlibm/s_atan.c

And also a GO implementation here:
https://golang.org/src/math/atan.go

It seems they use alternative methods in order to try to avoid calculating the same coefficients and use a pre-calculated number or maybe even a different method to approximate functions using polynomials (Remez algorithm). It seems Taylor series is only good for small numbers.

Some interesting links in:
https://stackoverflow.com/questions/35183929/approximating-atan-without-a-math-library

So, I think I have to use a different algorithm and calculate those coefficients depending on the precision you want.

I think with this library:
https://github.com/samhocevar/lolremez
you can generate the C/C++ function output. for atan function but probably using a double type. So I do not know how you can adapt it for this extensions in order to increase precision.

@josecelano
Copy link
Author

josecelano commented Apr 8, 2020

This paper explains al alternative way to calculate it:
https://digitalcommons.lmu.edu/cgi/viewcontent.cgi?article=1101&context=math_fac

And some python examples:
https://stackoverflow.com/questions/44249104/calculating-inverse-trigonometric-functions-with-formulas
But no idea what is the maximum precision possible with those methods.

@josecelano
Copy link
Author

It seems there is another C library for arbitrary precision that supports transcendental functions:

exp, log, pow, sin, cos, tan, asin, acos, atan, atan2

https://bellard.org/libbf/
https://github.com/rurban/libbf

@rtheunissen do you thing is it possible to link that library to this extension and add support for those transcendental functions easily?

@josecelano
Copy link
Author

I have found an implementation:

https://github.com/darlinghq/darling-bc/blob/master/bc/libmath.b#L176-L241

I think it could be the one used by the underlying package used by the other PHP arbitrary precision package: https://www.php.net/manual/es/book.bc.php

@JordanRL
Copy link

I have an implementation in Fermat, which is now adding support for ext-decimal.

A fast, arbitrary precision implementation of atan is not trivial. There are good ways to do it quickly, but only if you can limit precision. This is further complicated by the fact that precision is used in ext-decimal instead of scale.

The continued fractions, taylor series, etc. that will calculate atan all work on scale (since they guarantee accuracy of total decimal positions, not significant positions).

I am absolutely willing to help on algorithmic implementations of these however.

The best option is likely @josecelano's suggestion of an existing arbitrary precision C library.

In all honesty, I desperately want to merge the efforts of ext-decimal and Fermat, but I'm not sure that's possible so long as Decimal uses precision instead of scale.

Fermat definitely goes beyond the scope of Decimal however, as it covers statistical functions. The ext-stats PECL extension is no longer maintained, so this is actually a large missing aspect of math within PHP, but I'm unsure of the best approach to merging the effort between that, my own library, and ext-decimal.

@JordanRL
Copy link

JordanRL commented Jul 15, 2021

For context, the implementation of arcsin()/asin() first actually makes atan very easy:

<?php

$atan = asin( $x / ( 1 + sqrt(pow($x, 2)) ) );

To implement asin() as pseudo-code using Decimal objects in userland, it would look something like this:

<?php

use Decimal\Decimal;

// NOTE: Precision/sigfigs are very hard to do with trig/transcendental functions
// use scale instead.
function asin(string|int|float $x, int $scale): Decimal
{
            $prevAnswer = new Decimal($x, $scale);
            $answer = $prevAnswer;
            // This example has a hard-coded escape hatch at 15 loops, but this could be dropped
            $count = 0;
            $leadingZeros = new Decimal('0.'.str_pad('1', $scale, '0', STR_PAD_LEFT), $scale+2);

            do {
                $answer = $answer->sub(
                    $answer->sin()->sub($x)->div($answer->cos())
                );
                $diff = $answer->sub($prevAnswer)->abs();
                $prevAnswer = $answer->toFixed($scale);
                $increaseScale = ($diff->compareTo($leadingZeros) == 1);
                $count++;
            } while ($increaseScale && $count < 15);

            return $answer;
}

Doing the arc functions without having the normal trig functions is... kind of silly.

I think this feature may be unresolveable with the way mpdec does its calculations. It likely gets a lot of its speed through its method of using precision instead of scale, but scale is almost required to do any sensible transcendental function implementations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants