Make log also accept a base. (#1667)

Make `log` in `racket/base` optionally accept a second argument.

The second argument is the log `base`. The docs also recommend
`fllogb` when precision is important.

* Error message when base is 1
* Added docs.
* Add tests.
This commit is contained in:
Leif Andersen 2017-04-15 10:40:41 -04:00 committed by GitHub
parent 6492226411
commit 922b5f06e9
4 changed files with 54 additions and 11 deletions

View File

@ -35,7 +35,9 @@
"planet-doc"
"mzscheme-doc"
"compiler-lib"
"drracket"))
"drracket"
"math-doc"
"math-lib"))
(define pkg-desc "Base Racket documentation")

View File

@ -6,7 +6,8 @@
racket/unsafe/ops
racket/require
racket/random
racket/list))
racket/list
math/flonum))
@(define math-eval (make-base-eval))
@examples[#:hidden #:eval math-eval (require racket/math)]
@ -606,19 +607,29 @@ integer.}
Returns Euler's number raised to the power of @racket[z]. The result
is normally inexact, but it is exact @racket[1] when @racket[z] is an
exact @racket[0].
exact @racket[0]. See also @racket[expt].
@mz-examples[(exp 1) (exp 2+3i) (exp 0)]}
@defproc[(log [z number?]) number?]{
@defproc[(log [z number?] [b number? (exp 1)]) number?]{
Returns the natural logarithm of @racket[z]. The result is normally
inexact, but it is exact @racket[0] when @racket[z] is an exact
@racket[1]. When @racket[z] is exact @racket[0],
@exnraise[exn:fail:contract:divide-by-zero].
@mz-examples[(log (exp 1)) (log 2+3i) (log 1)]}
If @racket[b] is provided, it serves as an alternative
base. It is equivalent to @racket[(/ (log z) (log b))], but
can potentially run faster. If @racket[b] is exact
@racket[1], @exnraise[exn:fail:contract:divide-by-zero].
Consider using @racket[fllogb] instead when accuracy is
important.
@mz-examples[(log (exp 1)) (log 2+3i) (log 1) (log 100 10) (log 8 2) (log 5 5)]
@history[#:changed "6.9.0.1" @elem{Added second argument for arbitrary bases.}]}
@; ------------------------------------------------------------------------

View File

@ -2119,7 +2119,11 @@
(test +inf.0 real-part (log -inf.0))
(test +3142.0 round (* 1000 (imag-part (log -inf.0))))
(test +nan.0 log +nan.0)
(test 2.0 log 100 10)
(test 3.0 log 8 2)
(test 1.0 log 5 5)
(err/rt-test (log 0) exn:fail:contract:divide-by-zero?)
(err/rt-test (log 5 1) exn:fail:contract:divide-by-zero?)
(test 1 cos 0)
(test 1.0 cos 0.0)
@ -3386,6 +3390,12 @@
(test -4882.526517254422 real->double-flonum -13737024017780747/2813507303900)
(test -9.792844933246106e-14 real->double-flonum -1656/16910305547451097)
;; Arbitrary base log
(test (/ (log 5) (log 20)) log 5 20)
(test (/ (log 5) (log -8)) log 5 -8)
(test (/ (log 7) (log 3+5i)) log 7 3+5i)
(test (/ (log -5+2i) (log 12)) log -5+2i 12)
;; Hack to use the "math" package when it's available:
(when (collection-file-path "base.rkt" "math" #:fail (lambda (x) #f))
(eval

View File

@ -101,6 +101,7 @@ static Scheme_Object *numerator (int argc, Scheme_Object *argv[]);
static Scheme_Object *denominator (int argc, Scheme_Object *argv[]);
static Scheme_Object *exp_prim (int argc, Scheme_Object *argv[]);
static Scheme_Object *log_prim (int argc, Scheme_Object *argv[]);
static Scheme_Object *log_e_prim (int argc, Scheme_Object *argv[]);
static Scheme_Object *sin_prim (int argc, Scheme_Object *argv[]);
static Scheme_Object *cos_prim (int argc, Scheme_Object *argv[]);
static Scheme_Object *tan_prim (int argc, Scheme_Object *argv[]);
@ -649,12 +650,12 @@ scheme_init_number (Scheme_Env *env)
scheme_add_global_constant("exp",
scheme_make_folding_prim(exp_prim,
"exp",
1, 1, 1),
1, 1, 1),
env);
scheme_add_global_constant("log",
scheme_make_folding_prim(log_prim,
scheme_make_folding_prim(log_prim,
"log",
1, 1, 1),
1, 2, 1),
env);
scheme_add_global_constant("sin",
scheme_make_folding_prim(sin_prim,
@ -2754,7 +2755,7 @@ static Scheme_Object *un_exp(Scheme_Object *o)
static Scheme_Object *un_log(Scheme_Object *o)
{
return log_prim(1, &o);
return log_e_prim(1, &o);
}
static Scheme_Object *numerator(int argc, Scheme_Object *argv[])
@ -2791,7 +2792,8 @@ static Scheme_Object *complex_log(Scheme_Object *c)
m = magnitude(1, &c);
theta = angle(1, &c);
return scheme_bin_plus(log_prim(1, &m), scheme_bin_mult(scheme_plus_i, theta));
return scheme_bin_plus(log_e_prim(1, &m),
scheme_bin_mult(scheme_plus_i, theta));
}
static Scheme_Object *bignum_log(Scheme_Object *b)
@ -3027,13 +3029,31 @@ static Scheme_Object *scheme_single_inf_plus_pi()
#endif
GEN_UNARY_OP(exp_prim, exp, exp, scheme_inf_object, scheme_single_inf_object, scheme_zerod, scheme_zerof, scheme_nan_object, scheme_single_nan_object, complex_exp, GEN_ZERO_IS_ONE, NEVER_RESORT_TO_COMPLEX, BIGNUMS_AS_DOUBLES)
GEN_UNARY_OP(log_prim, log, SCH_LOG, scheme_inf_object, scheme_single_inf_object, scheme_inf_plus_pi(), scheme_single_inf_plus_pi(), scheme_nan_object, scheme_single_nan_object, complex_log, GEN_ONE_IS_ZERO_AND_ZERO_IS_ERR, NEGATIVE_USES_COMPLEX, BIGNUM_LOG)
GEN_UNARY_OP(log_e_prim, log, SCH_LOG, scheme_inf_object, scheme_single_inf_object, scheme_inf_plus_pi(), scheme_single_inf_plus_pi(), scheme_nan_object, scheme_single_nan_object, complex_log, GEN_ONE_IS_ZERO_AND_ZERO_IS_ERR, NEGATIVE_USES_COMPLEX, BIGNUM_LOG)
GEN_UNARY_OP(sin_prim, sin, SCH_SIN, scheme_nan_object, scheme_single_nan_object, scheme_nan_object, scheme_single_nan_object, scheme_nan_object, scheme_single_nan_object, complex_sin, GEN_ZERO_IS_ZERO, NEVER_RESORT_TO_COMPLEX, BIGNUMS_AS_DOUBLES)
GEN_UNARY_OP(cos_prim, cos, SCH_COS, scheme_nan_object, scheme_single_nan_object, scheme_nan_object, scheme_single_nan_object, scheme_nan_object, scheme_single_nan_object, complex_cos, GEN_ZERO_IS_ONE, NEVER_RESORT_TO_COMPLEX, BIGNUMS_AS_DOUBLES)
GEN_UNARY_OP(tan_prim, tan, SCH_TAN, scheme_nan_object, scheme_single_nan_object, scheme_nan_object, scheme_single_nan_object, scheme_nan_object, scheme_single_nan_object, complex_tan, GEN_ZERO_IS_ZERO, NEVER_RESORT_TO_COMPLEX, BIGNUMS_AS_DOUBLES)
GEN_UNARY_OP(asin_prim, asin, SCH_ASIN, scheme_nan_object, scheme_single_nan_object, scheme_nan_object, scheme_single_nan_object, scheme_nan_object, scheme_single_nan_object, complex_asin, GEN_ZERO_IS_ZERO, OVER_ONE_MAG_USES_COMPLEX, BIGNUMS_AS_DOUBLES)
GEN_UNARY_OP(acos_prim, acos, acos, scheme_nan_object, scheme_single_nan_object, scheme_nan_object, scheme_single_nan_object, scheme_nan_object, scheme_single_nan_object, complex_acos, GEN_ONE_IS_ZERO, OVER_ONE_MAG_USES_COMPLEX, BIGNUMS_AS_DOUBLES)
static Scheme_Object *
log_prim (int argc, Scheme_Object *argv[])
{
if (argc == 1) {
return log_e_prim(argc, argv);
} else {
Scheme_Object *a, *b;
a = argv[0];
b = argv[1];
if(SAME_OBJ(b, scheme_make_integer(1))){
scheme_raise_exn(MZEXN_FAIL_CONTRACT_DIVIDE_BY_ZERO,
"log: undefined for base 1");
ESCAPED_BEFORE_HERE;
}
return scheme_bin_div(un_log(a), un_log(b));
}
}
static Scheme_Object *
atan_prim (int argc, Scheme_Object *argv[])
{