Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

Given a floating point x (x<100), return e^x and ln(x). The first 6 decimal places of the number have to be right, but any others after do not have to be correct.

You cannot have any "magic" constants explicitly stated (e.x. a=1.35914 since 1.35914*2 ~= e), but you can calculate them. Only use +, -, *, and / for arithmetic operators.

If x is less than or equal to 0, output ERROR instead of the intended value of ln(x).

Test Cases:

input: 2
output: 7.389056 0.693147
input: 0.25
output: 1.284025 -1.386294
input: 2.718281828
output: 15.154262 0.999999  (for this to output correctly, don't round to 6 places)
input: -0.1
output: 0.904837 ERROR

Shortest code wins.

share|improve this question
1  
I assume you mean to exclude exponentiation of entirely (not just for the the calculation of e itself)? 2**(x*1.442695) for example seems a bit too easy. –  primo Nov 25 '12 at 18:04
    
@primo: yes, there is no exponentiation whatsoever, but things like product([2]*3) is ok. I guess I should rule out "magic constants" in the case of 1.442695. –  beary605 Nov 25 '12 at 18:35
1  
You might want to also specify a valid range for the input - any approximation can lose accuracy if x becomes too large or too small. –  arshajii Nov 26 '12 at 0:08
    
@A.R.S.: added a boundary. Thanks. –  beary605 Nov 26 '12 at 0:14
1  
No one uses APL!? Seriously? –  TwiNight Dec 25 '12 at 19:45

5 Answers 5

up vote 3 down vote accepted

JavaScript, 103 101 99 97 93 90

This implementation is based on primo's comprehensive description of the algorithm he used.

for(e=l=x=prompt(g=1e5);--g;y=x-1,l=(x>1?y/=x:-y)*l+y/g)
    e=1+e/g*x;
alert([e,x>0?l:"ERROR"])

Edit:
- Trying to catch Perl. Stole a byte back from primo by copying his branch. :)
- 2 more bytes courtesy of primo.
- Finally, caught primo's version! With primo's help, of course... :)
- Simplified the assignment to L. Shaved 3 more bytes.

share|improve this answer
    
It's somewhat humorous that defining Math.abs inline is actually shorter than using the built-in. –  primo Nov 27 '12 at 7:39
    
As far as catching perl goes, would it help if I pointed out that the initial values of e and l don't really matter? –  primo Dec 2 '12 at 6:58
    
@primo Nice catch! –  Paul Walls Dec 2 '12 at 8:13
    
I've got 93, by combining the y and l definitions: l=(x>1?y=1-1/x:-(y=x-1))*l+y/g –  primo Dec 14 '12 at 7:29
    
Very elegant. I probably spent over an hour trying to figure out a way to exploit the relationship between x<1 and y<0. You sir, are a genius! :) –  Paul Walls Dec 14 '12 at 9:37

PHP 109 107 bytes

<?for($g=1e4;--$g;$l=$y/$g+abs($y=$x>1?1-1/$x:$x-1)*$l)$e=1+$e/$g*$x.=fgets(STDIN);echo"$e ",$x>0?$l:ERROR;

is a fairly straight-forward calculation. I use a nested form of the sum of inverse factorials, which not only increases the convergence rate, but also allows for exponentiation at the same time:

is slightly more complicated. All convergent series seem to work for or , but not both (Newton's iteration does not have this limitation, but requires the calculation of each step). This isn't really a problem, though, given the log identity:

This means that if, for example, the iteration you're using only works on and , you can use the multiplicative inverse of and negate the result. Because I was using a nested identity for , I also chose to use a nested identity for :

where

Or equivalently, as demonstrated by Paul Walls' implementation:

I define the case as (which is necessarily negative), using the absolute value for the inner product, and then allowing a bare value in the fraction to correct the sign.

Sample I/O:

$ echo 2 | php exp_ln.php
7.3890560989307 0.69314718055995

$ echo 0.25 | php exp_ln.php
1.2840254166877 -1.3862943611199

$ echo 2.718281828 | php exp_ln.php
15.154262234523 0.99999999983113

$ echo -0.1 | php exp_ln.php
0.90483741803596 ERROR

Perl 95 93 89 bytes

$e=1+$e/$?*($x.=<>),$l=$_/$?+$l*abs,$_=$x>1?1-1/$x:$x-1while--$?;print"$e ",$x>0?$l:ERROR

Nearly identical to the PHP solution above, with a slightly larger iteration (65535 down to 0).

Edits:

  • Both 2 byte improvements due to Paul Walls.
  • Four more bytes saved in Perl by (ab)using $?, which is stored internally as an unsigned short, and by using $_ to save parentheses in abs.
share|improve this answer
    
+1 lovely explanation –  David Carraher Nov 26 '12 at 12:13
    
For x = 1e-4, I get php "1.0001000050002 -8.9909564381939" and perl "1.00010000500017 -8.99099322429857". Should be like "1.000100005 -9.21034037195" Anyways nice job taking advantage of ln x = -ln 1/x to use a nested expression –  miles Nov 26 '12 at 13:49
    
@milest x=1e-5 will be off by even more, because it's the same calculation as x=100000. As it is, x=1e-2 and x=100 are accurate to full floating point precision, and x=1e-3 and x=1000 are accurate to about 5 digits. If more accuracy is needed, the arbitrary 1e4 bound that I've chosen can be increased accordingly. –  primo Nov 26 '12 at 14:25

Python 2 (168 char)

basic implementation of power series

I need to learn a golfing language =/

I increased the bound to 1e-14 (twice from 1e-7) since some values were off a bit in 6 decimal places, works well for input from 1e-5 to 100 (slows down at input approaches 0)

x=input();t,r,i=1,0,1.
while abs(t)>=1e-14:t,r,i=t*x/i,r+t,i+1
if x<=0:s='Error'
else:z=(x-1.)/(x+1);t,s,i=2*z,0,1
while abs(t/i)>=1e-14:t,s,i=t*z*z,s+t/i,i+2
print r,s
share|improve this answer
    
Doesn't python support m=1e-7? –  dmckee Nov 25 '12 at 20:53
    
yes it does but 1e-7 can be thought of as 1*10^-7 which might be thought of as exponentiation, so I'm not really sure if its allowed here –  miles Nov 25 '12 at 20:59
    
I will allow for scientific notation. –  beary605 Nov 26 '12 at 0:02

Haskell, 166 (89 without I/O)

s=1e-7
e x|x>s=1/e(-x)|x>0=1+x|y<-e$x+s=y-y*s
l x|x<1=0-l(1/x)|1>0=sum$map(s/)[1,1+s..x]
n x|x<0="ERROR"|1>0=show$l x
main=interact$unwords.(\x->[show$e x,n x]).read

Takes an alternative approach: we use that

∂/∂x ex = ex

and solve the differential equation numerically, with a simple euler method. Similarly, use

ln x = 1x 1/x d‌x

and calculate the integral with the rectangular method.

Example:

$ echo 2 | ./exp-and-log
7.389056835370484 0.6931472554471929
$ echo 0.25 | ./exp-and-log
1.284025432730133 -1.3862944228194163
$ echo 2.718281828 | ./exp-and-log
15.15426430695358 1.0000000576151333
$ echo -0.1 | ./exp-and-log
0.9048374135134576 ERROR

It's quite amazingly inefficient, in fact it uses about 4 GB of memory even for these examples (since it's non-tail–recursive... you need to compile (in GHC) with -with-rtsopts=-K2G so it even accepts such a ridiculous stack size).

share|improve this answer

Surprised at no one using APL which has built-in exponentiation and natural logarithm, I will submit one.

(*x),⍟x←⎕
share|improve this answer
1  
Only use +, -, *, and / for arithmetic operators.? –  beary605 Dec 27 '12 at 17:12
1  
Oh didn't see that! Was falling back to Taylor expansion (which I thought would still favor APL due to built-in factorial, reciprocal and polynomial functions) when I found out the function breaks when calculating e^99 using 154 terms 'cuz 99^154>2^1024. Given up –  TwiNight Dec 27 '12 at 18:27

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.