log / exp / sqrt / power
Definition: |
atom res = log(atom x)
-- or -- atom res = ln(atom x) -- or -- atom res = log10(atom x) -- or -- atom res = log2(atom x) -- or -- atom res = exp(atom x) -- or -- atom res = sqrt(atom x) -- or -- atom res = power(atom x, y) -- or -- atom res = powmod(atom base, exponent, modulus) -- or -- atom res = mulmod(atom a, b, modulus) |
Description: |
log[10/2]() returns the natural or base 10/2 logarithm of x. exp() returns the inverse of log(), implemented trivially as power(EULER,x). sqrt() returns the square root of x. x must not be negative. power() returns the x raised to the power y. powmod() returns the equivalent of rmdr|mod(power(base,exponent),modulus) only much faster and more accurate. mulmod() returns the equivalent of rmdr|mod(a*b,modulus) only (sometimes somewhat slower and) more accurate. |
pwa/p2js: | Supported. |
Comments: |
These functions may be applied to an atom or sq_log[10/2](), sq_sqrt(), sq_power() to all
elements of a sequence. The rules for sequence operations apply. Note that logarithms are only defined for positive numbers. Your program will abort with a message should you try to obtain one of a negative number or zero. ln() is a simple alias of log(), likewise sq_ln() and hll_ln(), which emphasize they yield the natural logarithm; were it not for legacy code and compatibility issues, I’d probably just deprecate/delete the [sq_|hll_]log() names. log10() is a simple wrapper to log(), multiplied by (1/log(10)). Likewise log2(), which also contains some code to ensure that non-negative integer powers of 2 yeild an integer result [pre-1.0.2 log2(8) gave 3-(4.44e-16) on 32-bit, while 64-bit fared a bit better up to log2(8192) which gave 13-(8.67e-19), ie both pretty close but no cigar], covering 0..31 on 32-bit, and 0..63 on 64-bit. A slightly less efficient version of that code was also added to log10(), but only guarantees 0..9 on 32-bit (aka integer inputs of 1, 10, .. 1_000_000_000), and 0..19 on 64-bit. Should the (reduced) performance of log10/2 be an issue, you would be much better off by invoking ln() and performing said multiplication by a predefined constant inline. log() is directly supported by the floating point hardware and as such, without any wrapper/multiplication, may prove noticeably faster, and perhaps slightly more accurate. exp(atom x) is the inverse of log(), and is implemented simply as return power(EULER,x). There is no similar builtin function for the inverse of log10(); you are expected to use power(10,x) directly, likewise log2(). Also, there is currently no sq_exp() function, it has simply never been needed or asked for (plus it don’t quite fit in the seqops table). Powers of 2 are calculated very efficiently. Other languages have a ** or ^ operator to perform the same action as power(), though in some languages ^ is the xor function. It is also noted that any potential ambiguity in, say, "-5^2" simply does not occur in power(-5,2) vs. -power(5,2). Theoretically power(0,0) is undefined, however the result is 1, mainly for consistency with other programming languages. Attempting to raise any value <=0 to a negative or non integer power causes a fatal runtime error (same as python). Obvious workarounds exist, for instance should you require a function that returns the cubic root of negative (and positive) numbers: function cube_root(atom c) return sign(c)*power(abs(c),1/3) end function |
Examples: |
?log(100) -- prints 4.605170186 ?log10(100) -- prints 2 -- (exact in 1.0.2+) ?log2(8) -- prints 3 -- "" ?sqrt(16) -- prints 4 ?power(5,2) -- prints 25 ?powmod(13789,722341,2345) -- prints 2029 |
Implementation: |
log(): via :%opLog in builtins\VM\pTrig.e (an autoinclude). log10() and log2(): see builtins\log10.e (an autoinclude) for details of the actual implementation. exp(): see builtins\pmaths.e (an autoinclude) for details of the actual implementation. sqrt(): via :%opSqrt in builtins\VM\pTrig.e (an autoinclude). power(): via :%opPow in builtins\VM\pPower.e (an autoinclude). powmod() and mulmod(): see builtins\pmaths.e. NB: no formal statement regards matching mod() or rmdr() is made, in other words only lightly tested and currently only formally supported for all-positive parameters, but as ever I will be happy to fix any glitches that hinder a real-world need. One unknown is whether there should be a bool all_ints=true parameter, which would make them check the parameters are not fractional and do not exceed the "integer" precision limits of an atom. The constant EULER (renamed from E in 1.0.2) is defined in psym.e/syminit(), part of the compiler, as 2.71828182845904523536, the last two digits of which are probably beyond precision limits of 64-bit, with the last 6 digits likewise being of course pretty much irrelevant on 32-bit. There is also a commented-out (and undocumented) routine in mpfr.e which will generate a string version of that constant to however many digits you have patience for (easily portable to mpfr.js if needed). |