Finding e

In mathematics we have a few enigmatic numbers, of with pi and e are the best known ones. Pi is the ratio between diameter and circumference of the circle and e is the base of the natural logarithm. The nice part about 'e' is that it is defined in a simple mathematical formula:

And this formula is easy to implement in any programming language. Even in Java... So I set out to test my limited knowledge of Java and see how accurate Java math is.


Attempt 1: shE0.java

No beating around the bush. Let's get cracking. Here's the source of shE0.java:

class shE0
{
   public static void main ( String args [] )
     {
	float fraction, result = 1.0;
	int n;
	
	try 
	  {
	     n = Integer.parseInt (args [0]);
	  }

	catch (ArrayIndexOutOfBoundsException e)
	  {
	     System.out.println ("Usage : shE number ");
	     System.out.println ("Aborting.");
	     return;
	  }
	catch (NumberFormatException e)
	  {
	     System.out.println ("Error converting number " + args [0] + ". Aborting");
	     return;
	  }
	
	fraction = 1.0 + 1.0 / n;
	System.out.println ("Fraction = " + fraction);
	System.out.println ("Calculating 'e' using pow  : " + Math.pow (fraction, n));

	while (n > 0)
	  {
	     result = result * fraction;
	     --n;
	  }
	System.out.println ("Calculating 'e' using loop : " + result);
	System.out.println ("'e' according to Math.E    : " + Math.E);
     }
}
   
See this source: Java uses qualified imports. No IMPORT statement rquired though. Apparently the compiler is colossal enough to find out things by itself. Not very assuring, but this language is collosal so let's give it the benefit of the doubt. The qualified imports in this example were: Let's compile this source and see what we get:
jan@Beryllium:~/develop/java$ javac shE0.java
shE0.java:5: possible loss of precision
found   : double
required: float
        float fraction, result = 1.0;
                                 ^
shE0.java:25: possible loss of precision
found   : double
required: float
        fraction = 1.0 + 1.0 / n;
	                     ^
2 errors
jan@Beryllium:~/develop/java$ jed shE0.java
   
This is new to me. Error while compiling test programs. That's good. I like new experiences.. :o)

But what does this mean? I guess it means: This is an assuring experience. In C and C++ you can get away with this kind of programming. In Modula-2 you cannot. And Java also protests. That's a Good Thing! I prefer TYPE-safe languages. We're going to fix these error and name it shE1.java


Attempt 2: shE1.java

OK, so I have changed the source and to keep this webpage within the reasonable, I only list the changes:

class shE1

	float fraction, result = 1.0f;
	...
	...
	fraction = 1.0f + 1.0f / (float) n;
   
Let's see if this compiles. Programming requires a lot of trial and error tests, as you will find out. Keep your fingers crossed!
jan@Beryllium:~/develop/java$ javac shE1.java
jan@Beryllium:~/develop/java$
   
Now, THAT's more like it. No errors during compilation. Let's see how java runs my source:
jan@Beryllium:~/develop/java$ java shE1
Usage : shE1 number
Aborting.

jan@Beryllium:~/develop/java$ java shE1 10
Fraction = 1.1
Calculating 'e' using pow  : 2.593743022278593
Calculating 'e' using loop : 2.5937428
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shE1 1000
Fraction = 1.001
Calculating 'e' using pow  : 2.717050770326506
Calculating 'e' using loop : 2.7170494
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shE1 100000
Fraction = 1.00001
Calculating 'e' using pow  : 2.7219622037713953
Calculating 'e' using loop : 2.7219107
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shE1 10000000
Fraction = 1.0000001
Calculating 'e' using pow  : 3.293967694905941
Calculating 'e' using loop : 2.8841858
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shE1 1000000
Fraction = 1.000001
Calculating 'e' using pow  : 2.595226670281386
Calculating 'e' using loop : 2.5898523
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shE1 100000
Fraction = 1.00001
Calculating 'e' using pow  : 2.7219622037713953
Calculating 'e' using loop : 2.7219107
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$
   
The first test was obvious: I forgot to specify the value for 'n'. But the program caught my error and corrected me. Good. Try and catch are working. In the next lines I run shE1 with several values of 'n'. The value of 'fraction' is calculated correct here. On another machine, 1 + 1/100 was 1.010001265 which of course is false. The values for 'e' gets better when n goes up. That's in accordance with the theory. But the accuracy goes down again when n exceeds 100,000 (a hundred thousand). Hmm strange. Apparently the fraction is difficult to express in binary. Also: look at what Math.pow returns and what our loop does: the loop value stays within reason from the real value. For 'n' is 10 million, Math.pow returns a value of 'e' that exceeds pi!


Attempt 3: shEd.java

I call this attempt 3, but in reality it was attempt 20 odd. I ran the tests on my laptop and I remember that I got a lot of very stange results.. So I had to check more versions than on this Pentium IV system. Still, here's the new version of shE, which is now called 'shEd' since it switches over to doubles.

class shEd
{
   public static void main ( String args [] )
     {
	double fraction, result = 1.0d;
	int n;
	
	try 
	  {
	     n = Integer.parseInt (args [0]);
	  }

	catch (ArrayIndexOutOfBoundsException e)
	  {
	     System.out.println ("Usage : shE number ");
	     System.out.println ("Aborting.");
	     return;
	  }
	catch (NumberFormatException e)
	  {
	     System.out.println ("Error converting number " + args [0] + ". Aborting");
	     return;
	  }
	
	fraction = 1.0d + 1.0d / (double) n;
	System.out.println ("n = " + n + " and fraction = " + fraction);
	System.out.println ("Calculating 'e' using pow  : " + Math.pow (fraction, n));

	while (n > 0)
	  {
	     result = result * fraction;
	     --n;
	  }
	System.out.println ("Calculating 'e' using loop : " + result);
	System.out.println ("'e' according to Math.E    : " + Math.E);
     }
}
   
Let's see how this compiles:
jan@Beryllium:~/develop/java$ javac shEd.java
jan@Beryllium:~/develop/java$
   
That's a good thing. Let's now run it:
jan@Beryllium:~/develop/java$ java shEd
Usage : shE number
Aborting.

jan@Beryllium:~/develop/java$ java shEd 10
n = 10 and fraction = 1.1
Calculating 'e' using pow  : 2.5937424601000023
Calculating 'e' using loop : 2.5937424601000023
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shEd 100
n = 100 and fraction = 1.01
Calculating 'e' using pow  : 2.7048138294215285
Calculating 'e' using loop : 2.7048138294215294
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shEd 1000
n = 1000 and fraction = 1.001
Calculating 'e' using pow  : 2.7169239322355936
Calculating 'e' using loop : 2.7169239322355985
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shEd 10000
n = 10000 and fraction = 1.0001
Calculating 'e' using pow  : 2.7181459268249255
Calculating 'e' using loop : 2.7181459268248984
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shEd 100000
n = 100000 and fraction = 1.00001
Calculating 'e' using pow  : 2.7182682371922975
Calculating 'e' using loop : 2.718268237192295
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shEd 1000000
n = 1000000 and fraction = 1.000001
Calculating 'e' using pow  : 2.7182804690957534
Calculating 'e' using loop : 2.7182804690959363
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shEd 10000000
n = 10000000 and fraction = 1.0000001
Calculating 'e' using pow  : 2.7182816941320818
Calculating 'e' using loop : 2.7182816941320103
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shEd 100000000
n = 100000000 and fraction = 1.00000001
Calculating 'e' using pow  : 2.7182817983473577
Calculating 'e' using loop : 2.71828179834636
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shEd 1000000000
n = 1000000000 and fraction = 1.000000001
Calculating 'e' using pow  : 2.7182820520115603
Calculating 'e' using loop : 2.7182820520118995
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shEd 10000000000
Error converting number 10000000000. Aborting
jan@Beryllium:~/develop/java$
   
It compiles good and it runs good. No arguments triggers the catcher. And a too great argument (10 billion, which exceeds the range of the integer values) also makes us get kicked out. That's a nice thing and it's built in the language.
Looks great! But how much are the values off? I could shell outn the HP 11c and an hour of my time, but it's easier to make a new version of shEd....


Attempt 4: shEd2.java

In this source, I calculate the difference between the calculated values with the value defined in Math.E. Here's the source:

class shEd1
{
   public static void main ( String args [] )
     {
	double mpval, fraction, delta1, delta2, result = 1.0d;
	int n;
	
	try 
	  {
	     n = Integer.parseInt (args [0]);
	  }

	catch (ArrayIndexOutOfBoundsException e)
	  {
	     System.out.println ("Usage : shE number ");
	     System.out.println ("Aborting.");
	     return;
	  }
	catch (NumberFormatException e)
	  {
	     System.out.println ("Error converting number " + args [0] + ". Aborting");
	     return;
	  }
	
	fraction = 1.0d + 1.0d / (double) n;
	mpval = Math.pow (fraction, n);
	delta1 = mpval - Math.E;
	System.out.println ("n = " + n + " and fraction = " + fraction);
	System.out.println ("Calculating 'e' using pow  : " + mpval + " delta = " + delta1);

	while (n > 0)
	  {
	     result = result * fraction;
	     --n;
	  }
	delta2 = result - Math.E;
	System.out.println ("Calculating 'e' using loop : " + result + " delta = " + delta2);
	System.out.println ("'e' according to Math.E    : " + Math.E);
     }
}
   
The red lines were changed. Let's see how we do now:
jan@Beryllium:~/develop/java$ java shEd1 10
n = 10 and fraction = 1.1
Calculating 'e' using pow  : 2.5937424601000023 delta = -0.12453936835904278
Calculating 'e' using loop : 2.5937424601000023 delta = -0.12453936835904278
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shEd1 100
n = 100 and fraction = 1.01
Calculating 'e' using pow  : 2.7048138294215285 delta = -0.01346799903751661
Calculating 'e' using loop : 2.7048138294215294 delta = -0.013467999037515721
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shEd1 1000
n = 1000 and fraction = 1.001
Calculating 'e' using pow  : 2.7169239322355936 delta = -0.0013578962234515046
Calculating 'e' using loop : 2.7169239322355985 delta = -0.0013578962234466196
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shEd1 1000000
n = 1000000 and fraction = 1.000001
Calculating 'e' using pow  : 2.7182804690957534 delta = -1.359363291708604E-6
Calculating 'e' using loop : 2.7182804690959363 delta = -1.3593631087438496E-6
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shEd1 10000000
n = 10000000 and fraction = 1.0000001
Calculating 'e' using pow  : 2.7182816941320818 delta = -1.3432696333026684E-7
Calculating 'e' using loop : 2.7182816941320103 delta = -1.3432703482862962E-7
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shEd1 100000000
n = 100000000 and fraction = 1.00000001
Calculating 'e' using pow  : 2.7182817983473577 delta = -3.011168736577474E-8
Calculating 'e' using loop : 2.71828179834636 delta = -3.0112685234229275E-8
'e' according to Math.E    : 2.718281828459045

jan@Beryllium:~/develop/java$ java shEd1 1000000000
n = 1000000000 and fraction = 1.000000001
Calculating 'e' using pow  : 2.7182820520115603 delta = 2.2355251516614771E-7
Calculating 'e' using loop : 2.7182820520118995 delta = 2.2355285445030404E-7
'e' according to Math.E    : 2.718281828459045
   
Some observations: But the differences are minimal. The loop method is only a lot longer:
jan@Beryllium:~/develop/java$ time java shEd1 1000000000
n = 1000000000 and fraction = 1.000000001
Calculating 'e' using pow  : 2.7182820520115603 delta = 2.2355251516614771E-7
Calculating 'e' using loop : 2.7182820520118995 delta = 2.2355285445030404E-7
'e' according to Math.E    : 2.718281828459045

real    0m5.254s
user    0m5.180s
sys     0m0.016s
jan@Beryllium:~/develop/java$
   
Five seconds calculation time on a 3 GHz dual core Pentium IV... Imagine how long that would take on a 3 MHz Sinclair ZX 81...


Conclusions

All in all, Java does not perform that bad... It's all in the book that you are reading to learn the language. I started out with many books that used a top down approach to teach Java. Always looking back to C, C++ and other languages. The "Java in easy steps" book uses a normal method: it teachs simple things that get complexer by the chapter. This seems to work for me.

Java

As long as you have some good books. Period.


Page created on 4 June 2010 and

Page equipped with FroogleBuster technology