-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
Currently, math:log and math:sqrt use the C functions log and sqrt. This is fine, but in the presence of bignums (as Erlang has), it becomes possible to wider their domains:
-
$10^{2480}$ cannot be used as an argument tomath:log, only because it is not representable as an IEEE double float. But it is representable as an Erlang bignum, and the result is an IEEE double float:
$log(10^{2480})$ is close to$5710.411030625233$
So it would perhaps be possible to makemath:logaccept larger numbers (a lot of them, since log grows, well, logarithmically :) -
$2^{2030}+111111111111111111111111117$ does not fit a double either. But similarly, it is an Erlang integer, and its square root does fit an IEEE double float (it is close to$3.511119404027961 \times 10^{305}$ ).
I'm not saying, of course, that getting this to work is "easy". Anyway, here's a survey on how many different Scheme implementations deal with it in different ways (but no implementation details here):
https://github.com/schemedoc/surveys/blob/master/surveys/log-requires-floating-point.md
And I can explain how STklos Scheme gets it done (because I have implemented these two enhancements):
- STklos
logof bignums is very simple, but it uses the GMP (I know Erlang doesn't):
static double my_bignum_log(SCM z) {
/* The GMP function mpz_get_d_2exp is similar to the C library
function frexp: it returns a float D between 0.5 and 1.0,
and sets an exponent E. Then,
N = D * 2^E
is the (possibly truncated) value of the original number.
Then calculating the log is simple:
log(N) = log(D*2^E)
= log(D) + log(2^E)
= log(D) + log(2) * E */
long expo;
double d = mpz_get_d_2exp(&expo, BIGNUM_VAL(z));
return log(d) + log(2) * (double) expo;
}But since Erlang has its own bignum implementation, maybe it won't be hard to get this working (?)
- STklos
sqrttries a couple of methods (like identifying perfect squares etc), and when all fail, it uses Newton's method. It is slow (because it is computed using bignum rationals), but we get some more answers for the function.
/* Ok, we tried everything. There's only the slow path now! */
SCM x = x0;
SCM x_new = x0;
SCM err = x0;
/* Approximate the square root... Essentially, Newton's method,
but coded using STklos' internal sub2, div2, mul2, abs
functions. */
while(STk_numgt2(err, rational_epsilon) > 0 &&
isfinite(REAL_VAL(exact2inexact(x_new)))) {
x_new = sub2(x, div2(sub2(mul2(x, x), z),
mul2(x, MAKE_INT(2))));
err = STk_abs(sub2(x_new, x));
x = x_new;
}
/* Return inexact, because if we got here, the square of this
result will not equal to z (it's an approximation, so it would
be strange to give an "exact" result that is not "exactly" the
result of the operation). But for floating-point, it is
acceptable to offer an approximation. */
return exact2inexact(x_new);Here mul2, div2, sub2 are generic 2-argument functions, and x is a bignum (so the computation is fully rational until the last moment, when exact2inexact converts it to a double).
Also,
SCM x = x0;Here, x0 is computed earlier using mpz_sqrt, so it is the truncated square root.
For reference, the math code for STklos (the C part) is here:
https://github.com/egallesio/STklos/blob/master/src/number.c
By no means I believe Erlang "needs" this -- I see it as a low-priority enhancement; and I also wouldn't say that my implementation for STklos makes sense for Erlang... Just wanted to share, in case the Erlang implementors find it to be useful.