Skip to main content

kabutz's Blog

Fibonacci (1000000000) Challenge

Posted by kabutz on February 24, 2012 at 11:44 AM PST

We all know the standard Fibonacci recursive algorithm:

  public BigInteger f(int n) {
    if (n == 0) return BigInteger.ZERO;
    if (n == 1) return BigInteger.ONE;
    return f(n - 1).add(f(n - 2));
  }

Your challenge is to find the first 10 bytes and the last 10 bytes of f(1_000_000_000). That's right, fibonacci of one billion. Please post the answer here, in hexadecimal.

My solution took 2500 seconds on an 8-core machine, utilizing all the cores.

Winner will get a special mention in my next Java Specialists' Newsletter (http://www.javaspecialists.eu).

One last hint. Fork/Join is very useful to speed up your calculation.

Comments

When is the Fibonacci (1000000000000) challenge coming? ...

When is the Fibonacci (1000000000000) challenge coming? :)

You wouldn't be able to do it in one line with GMP anymore, but it wouldn't be hard to implement a GMP-based solution.

Wes

Current results: 1000000 fibonacci ...

Current results:

1000000 fibonacci numbers
-------------------------
gcc 4.5.2 + gmp                         :   0.01 sec
ruby 1.9.3                              :   0.17 sec
java 1.6 -client (jscience LargeInteger):   0.18 sec
python 2.5                              :   0.22 sec

1000000000 fibonacci numbers
----------------------------
gcc 4.5.2 + gmp                         :    36  sec. (1 core E7200@3GHz)
gcc 4.5.2 + gmp                         :    55  sec. (1 core )
ruby 1.9.3                              :  3108  sec. (1 core )
java 1.6 -server (jscience LargeInteger):  8980  sec. (3 cores )
python 2.5                              : 11910 sec.  (1 core )

PS. I love this drupal blog :) the worst thing that I've ever seen...

I switched to "the well-known formulas" ...

I switched to "the well-known formulas" mentioned by Prof. Edsger W. Dijkstra and reduced almost 2/3 of time on the same machine.

This time it took 3308 seconds on the same 4 core i5-2520m@2.5GHz.  This is using Java 6 with JScience LargeInteger without any other concurrent handlings.  I am posting Java code here so that you can compare apple to apples.

private int[] KNOWN_FIBS = new int[] { 0, 0, 1 };

public LargeInteger F (int n) {

    if (n < KNOWN_FIBS.length) {
        return LargeInteger.valueOf (KNOWN_FIBS[n]);
    }

    LargeInteger[] j0j1 = F2 (n >> 1);
    LargeInteger j0 = j0j1[0];
    LargeInteger j1 = j0j1[1];
    LargeInteger fn;

    if ((n & 1) == 0) {
        fn = j0.times (j0).plus (j1.times (j1));
    } else {
        fn = j0.shiftLeft (1).plus (j1).times (j1);
    }

    return fn;
}

private LargeInteger[] F2 (int n) {

    if (n < KNOWN_FIBS.length - 1) {
        return new LargeInteger[] { LargeInteger.valueOf (KNOWN_FIBS[n]), LargeInteger.valueOf (KNOWN_FIBS[n + 1]) };
    }

    LargeInteger[] j0j1 = F2 (n >> 1);
    LargeInteger j0 = j0j1[0];
    LargeInteger j1 = j0j1[1];

    LargeInteger fn0 = j0.times (j0).plus (j1.times (j1));
    LargeInteger fn1 = j0.shiftLeft (1).plus (j1).times (j1);

    if ((n & 1) == 1) {
        LargeInteger fn2 = fn1.plus (fn0);
        fn0 = fn1;
        fn1 = fn2;
    }

    return new LargeInteger[] { fn0, fn1 };
}

   rexyoung, could you please test the following ...

rexyoung, could you please test the following code on your PC (my is pretty old :)?

 *  Compilation:  javac FibonacciJ.java
*  Execution:    java -server FibonacciJ N
*
*  Compute Fibonacci number using Dijkstra's recurrence:
*    F(2N-1)  = F(N-1)^2 + F(N)^2
*    F(2N)    = (2 F(N-1) + F(N)) F(N)
*
*  Reference: http://www.cs.utexas.edu/users/EWD/ewd06xx/EWD654.PDF
*  Reference: http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibFormula.... *
*************************************************************************/


import java.util.HashMap;
import org.jscience.mathematics.number.LargeInteger;


public class FibonacciJ {
    private static HashMap<LargeInteger, LargeInteger> cache = new HashMap<LargeInteger, LargeInteger>();
    private static LargeInteger ONE  = LargeInteger.ONE;
    private static LargeInteger ZERO = LargeInteger.ZERO;

    public static LargeInteger fibonacci(LargeInteger n) {
        if (n.equals(ZERO)) return ZERO;

        if (n.equals(ONE))  return ONE;
        if (cache.containsKey(n)) return cache.get(n);

        // odd
        if (!n.isEven()) {
            LargeInteger n2 = n.shiftRight(1);
            LargeInteger n3 = n2.plus(ONE);
            LargeInteger result = fibonacci(n2).times(fibonacci(n2)).plus(fibonacci(n3).times(fibonacci(n3)));
            cache.put(n, result);
            return result;
        }

        // even

        else {
            LargeInteger n2 = n.shiftRight(1);
            LargeInteger n3 = n2.minus(ONE);
            LargeInteger result = fibonacci(n2).times(fibonacci(n2).plus(fibonacci(n3).plus(fibonacci(n3))));
            cache.put(n, result);
            return result;
        }
    }


    public static void main(String[] args) {
        long t = System.nanoTime();
        LargeInteger N = LargeInteger.valueOf(Integer.parseInt(args[0]));
        LargeInteger res = fibonacci(N);
        byte[] res_array = new byte[res.bitLength()/8+1];
        res.toByteArray(res_array, 0);
        System.out.println("Time (ms): " + (System.nanoTime()-t)/1E6);
    }
}

Alexey, on the same machine, your program took 12081 ...

Alexey, on the same machine, your program took 12081 seconds.  I inserted some print outs you can refer to in the attachment.

Agreed, Alexey.  It is singularly the worst forum I've ...

Agreed, Alexey.  It is singularly the worst forum I've ever encountered.  Worst of breed.

I think the difference between GMP and Java can probably be explained by the use of better algorithms.  It would be quite interesting to open up the lid and see what GMP uses.  But it's been years since I've read C code, and my Drupal has already exhausted my tolerance for pain, so I won't volunteer to scratch around.

So, I guess Java + JNI + GMP will also return in 36 seconds :-)

I wonder if the GMP algorithm can be parallelized at all?

I used a laptop to get my results.  It has an Intel ...

I used a laptop to get my results.  It has an Intel i5-2520m (4 cores) @ 2.5GHz.  The java program uses about 2G memory.  I may add details about my home made algorithm and my handling of concurrency later.  Thanks.

F(14) = 377
unit time: 0 ms.
F(29) = 514229
unit time: 0 ms.
F(59) = 956722026041
unit time: 0 ms.
F(119) = 3311648143516982017180081
unit time: 0 ms.
F(238) = 10C780CAE80B7017CA42 ... 1BAA4297D9C6FA2032DF
unit time: 16 ms.
F(476) = 02758DE1D47CBAC7F9CD ... 6A55D1FB9EC25180AEBD
unit time: 0 ms.
F(953) = 15E16E41126CF2E4313B ... 91894AF6AAFEDFE6838D
unit time: 0 ms.
F(1,907) = 06C42BA74C0CA63E0AA5 ... 457AEDD9F337406B4BE9
unit time: 0 ms.
F(3,814) = 665F8B43C1A7F5654214 ... 2EB0CE1D10C9504513CF
unit time: 15 ms.
F(7,629) = 00941DFE96BE1B96E78F ... 4DA30AF4FCCE09739882
unit time: 0 ms.
F(15,258) = 00BFA069BC70F7FA0043 ... 3D5771A07FAAB83E5778
unit time: 16 ms.
F(30,517) = 0206F8F43B2F108D3228 ... B10100E472B06020DFD9
unit time: 15 ms.
F(61,035) = 0EDE75E43FA43F4B2596 ... AB1191D10C6543AFA6C2
unit time: 16 ms.
F(122,070) = 01EE5D71A76260C01921 ... 421212B189550116AFC8
unit time: 31 ms.
F(244,140) = 0856B7C9B36476D47527 ... 8DADDBE1E15378995410
unit time: 63 ms.
F(488,281) = 00FB941F6EF505D87517 ... 53D98C58A7A73A120091
unit time: 62 ms.
F(976,562) = 0228D4C126ADF2F8D0FF ... AF9C3FB91F81914EEE61
unit time: 109 ms.
F(1,953,125) = 10DF577CBAD3002AF87D ... 2553094299AABDF1D2C5
unit time: 297 ms.
F(3,906,250) = 027C8FAEC9FF639EFADA ... 473A0DAD6F8B2991ADB7
unit time: 842 ms.
F(7,812,500) = 0DD35DA39ADC01AA6EE0 ... 6374ED17411773D4516D
unit time: 2325 ms.
F(15,625,000) = 01AB6BCDC368CB820F38 ... 7C180848838F15CADCCB
unit time: 7051 ms.
F(31,250,000) = 063BB8959851F1B3A525 ... F61276CB3A2EB13D1DE5
unit time: 20 seconds.
F(62,500,000) = 56E13D2A6F85C7A4B9AD ... 793B4E9EDF34A81DE67B
unit time: 62 seconds.
F(125,000,000) = 41EE144C219F1C6C71F7 ... EDD873EE3E142834A645
unit time: 187 seconds.
F(250,000,000) = 25F7A935CA7BA33B4E48 ... 48821240557A9BBC633B
unit time: 576 seconds.
F(500,000,000) = 0C97594C04B5CE74CBEB ... AFFD3848AC567D65EFC5
unit time: 1881 seconds.
F(1,000,000,000) = 016280B82D8CBE0EDC1B ... A9532DF4D2D25B5DB63B
unit time: 6180 seconds.

Total Time: 8919 seconds.

Great job, rexyoung.  Are all 4 cores being used to do ...

Great job, rexyoung.  Are all 4 cores being used to do the calculation?  Where is your bottleneck?

My program uses JScience LargeInteger which seems use all ...

My program uses JScience LargeInteger which seems use all cores available.  However it does not use cores hard enough.  I observed an overall CPU usage between 60% and 80%.

The complexity of my algorithm is O(lg N).  Each step consists of several multiplications of LargeInteger.  It is based on that when F(n), F(n-1), and F(n-2) are known, then

F(2n-2) = F(n) * F(n-1) + F(n-1) * F(n-2)
F(2n-1) = F(n) * F(n) + F(n-1) * F(n-1)
F(2n) = F(2n-1) + F(2n-2)

I identified the formula after I wrote down F(1)...F(20) on a piece of paper.  Obviously it is not optimized enough.  But that was a side issue to me.  I was more interested in parallelizing the multiplications in each step.  I used my own project FastMessenger (which allows developers writing concurrent / parallel programs without using multi-threading http://java.net/projects/fastmessenger) for parallelization of this part.  Finally CPU usage was above 95% most of time, and I got a result of 8919 seconds.

 

Heinz, thank you for bringing up the interesting challenge.  I think I will use Fibonacci as one of examples I am going to blog for promotion of FastMessenger.

Thanks,
Rex

Hello My algorithm with BigInteger and matrix ...

Hello

My algorithm with BigInteger and matrix exponentiation takes 1286 milliseconds to calculate Fibonacci of 1000000.

--- My lame attempt :)   Windows 7. Intel Q9550 (12M ...

---

My lame attempt :)

Windows 7. Intel Q9550 (12M Cache, 2.83 GHz, 1333 MHz FSB).

Time: Ruby 1.9.3 - 3595 sec, gcc + gmp - 52 sec.

Source size: Ruby - 818 bytes, c - 126 bytes.

Output file size: 186 MB

First 10 bytes in hex: 481DF34ACFB62F5C5439

Last 10 bytes in hex: 0280A026FB794034F91D

Hi Alexey, your numbers do not match my result.  Could ...

Hi Alexey,

your numbers do not match my result.  Could you please post your Ruby and C code here?  http://www.javaspecialists.eu/enquiry.jsp

Heinz

  Sorry, sent something wrong first time ;) The ...

Sorry, sent something wrong first time ;) The correct sequence should be:

16280b82d8cbe0edc1b8...a9532df4d2d25b5db63b

Is that correct?

Yes, except the very first byte is 01, not 16, but the rest ...

Yes, except the very first byte is 01, not 16, but the rest is correct.

Would you mind sharing the Ruby and C code?

Heinz

The both algorithms based on that ...

The both algorithms based on that description:

http://www.gnu.org/software/gmp/manual/html_node/Fibonacci-Numbers-Algorithm.html

The c code just uses built-in gnu mp function mpz_fib_ui:

#include <gmp.h>
int main()
{
  mpz_t f;
  mpz_init(f);
  mpz_fib_ui(f, 1000000000);
  gmp_printf("%Zx\n", f);
  return 0;
}

Calculation time is ~50 sec (Q9550 2.8GHz).

But the Ruby code and performance is more interesting:

class Integer
  FC = Hash.new do |hash, key|
    if hash.has_key?(key - 1) and hash.has_key?(key - 2)
      hash[key] = hash[key - 1] + hash[key - 2]
    elsif hash.has_key?(key + 1) and hash.has_key?(key + 2)
      hash[key] = hash[key + 2] - hash[key + 1]
    else
      subkey = key.div(2)
      case key.modulo(4)
        when 1
          hash[key] = (2*hash[subkey] + hash[subkey - 1])*(2*hash[subkey] - hash[subkey - 1]) + 2
        when 3
          hash[key] = (2*hash[subkey] + hash[subkey - 1])*(2*hash[subkey] - hash[subkey - 1]) - 2
        else
          hash[key] = hash[subkey] * (hash[subkey] + 2*hash[subkey - 1])
      end
    end
  end
  FC[0] = 0
  FC[1] = 1
  def fib
    return FC[self]
  end
end
start_time = Time.now
puts 1000000000.fib.to_s(16)
puts "Time (s):",(Time.now - start_time)

Execution time is 3189 sec only (not much slower than your java 8 threads app :)

PS. That's Drupal blog is awful, doesn't work under Chrome, isn't clear how to format code etc.

Hi Alexey, I'll disqualify your C version, if you don't ...

Hi Alexey,

I'll disqualify your C version, if you don't mind, as it is simply a method call.  But your Ruby version is good.  You have the advantage of a reasonable BigInteger implementation in Ruby.  The Java BigInteger multiply is O(n2), so I had to rewrite that with Karatsuba.  Fortunately I was able to parallelize it with Fork/Join.  How hard would that be to do parallelize your solution in Ruby?  It should at least double your speed and then it should hopefully beat my Java solution.

I've added your caching idea to my solution, which seems to speed it up a little big.

May I ask what version of Ruby you are using?  I tried this with several versions on Ubuntu and it was a lot slower than your version.

Heinz

 

Heinz, I use the following Ruby version for ...

Heinz,

I use the following Ruby version for Windows:

ruby 1.9.3p0 (2011-10-30) [i386-mingw32]

Small remark - the original ruby solution isn't mine (the author is C Erler (2006)), it's just the tiny modification of his well-known and pretty old ruby example :) My task was to find the right tool and get the job done as quick as possible, and not reinvent the wheel.

PS. C one-liner version takes 36 seconds only (on C2D E7200@3Ghz :)

Alexey.

Tsk, tsk, tsk.  I was hoping that someone would at ...

Tsk, tsk, tsk.  I was hoping that someone would at least go to the trouble of coding a clever algorithm.  Or maybe take the Ruby example and parallelize it.

The clever algorithm was already invented by dr.Edsger ...

The clever algorithm was already invented by dr.Edsger W.Dijkstra in 1978 (http://www.cs.utexas.edu/users/EWD/ewd06xx/EWD654.PDF) :) Just for fun - a small benchmarking of 1 million fibonacci numbers:

c - 0.01 sec
ruby - 0.17 sec
python - 0.26 sec
jruby - 8.60 sec
java - 9.00 sec

PS. ruby and jruby use the same file :)

 I am working of the same algorithm but using both ...

I am working of the same algorithm but using both caching and LongInteger from JScience on my dual core mac 1 million takes 200-250 milliconds.

Just using JScience LongInteger its 640-770 milliseconds.

I am now working on using a sliding cache otherwise the map saving previous results eat all memory.

I also tried quite a few other algorithms all with worse performance.

This is a more interesting puzle than I expected.

Jerven, if you run it with -Xloggc:fib.vgc and then open ...

Jerven, if you run it with -Xloggc:fib.vgc and then open that file in HPJmeter, you will see that the object creation rate is pretty close to maximum.  Thus, as an earlier commenter suggested, the only way to make this really fast is to have a mutable big integer that can avoid constructing new BigIntegers all the time.  And of course we need to use decent algorithms.

Dijkstra's algorithm is O(log n).  The Karatsuba long multiplication algorithm used in Karatsuba is approximately O(n1.585), as opposed to the standard BigInteger O(n2) multiplication algorithm.

Both of these can be parallelized.  However, we will still push against the limits at which we can create objects.

 Karatsuba in JScience is parallized. And I was trying ...

Karatsuba in JScience is parallized. And I was trying to get github.com/tbuktu/bigint into a local JDK install to see how that works.

And so it is, thanks to Javolution's ConcurrentContext. ...

And so it is, thanks to Javolution's ConcurrentContext.  I missed that - thanks :-)

I managed to get the bigint compiled, by fixing an invalid method call to MutableBigInteger, which was probably due to a difference in Java versions.  The performance is quite good.

For the million's Fibonacci, we complete in 0.097 seconds.

With the stock-standard BigInteger, it completes this in 0.4 seconds.

Strange, I don't see any difference between that ...

Strange, I don't see any difference between that code github.com/tbuktu/bigint (9.4 sec) and built-in BigInteger (10 sec) for 1 mln Fibonacci.

Alexey, are you calling it with the -Xbootclasspath/p: ...

Alexey, are you calling it with the -Xbootclasspath/p: option to make sure it uses the doctored BigInteger?

 No, I've just removed the package string from fixed ...

No, I've just removed the package string from fixed sources and the import java.math.BigInteger string and recompiled the application :)

Yeah, the algorithm is ancient.  Considering that we ...

Yeah, the algorithm is ancient.  Considering that we have a half-life of 3 years in CS knowledge, this one is like the discovery of fire :-)  As I said: I was hoping someone would code a clever algorithm, not necessarily invent one.

However, the reason why Java is so slow is due to the BigInteger implementation.  The default multiply implementation is O(n2).  The org.jscience.* LargeInteger class has Karatsuba built in, but single-core.  Both the Karatsuba algorithm and Dijkstra's Fibonacci are excellent candidates for parallel execution to take advantage of multiple cores.

Has anyone heard of a good fast implementation of BigInteger that can get us results that are comparable with at least Ruby, maybe even C++?

     

Heinz, is this open to any JVM language or just Java proper?

Heinz, is this open to any JVM language or just Java proper?

Scrotty, do it in C# if you like, or Groovy, Scala, ...

Scrotty, do it in C# if you like, or Groovy, Scala, Assembler.  Whatever you like

A far more useful way to speed up the calculation is to not ...

A far more useful way to speed up the calculation is to not use recursion in the first place! With my highly inelegant serial approach consuming only a single core on a two year-old PC, I get the value in 1.3 seconds.

I don't mean to be a wiseass about it, but it's important to note the importance of choosing the right algorithm over the latest coding tricks (and fork-join is definitely a nice one).

Absolutely, snagiel.  You're not being a ...

Absolutely, snagiel.  You're not being a "wiseass" at all.  This question has everything to do with complexity and algorithms, rather than brute force or clever coding tricks with fork/join.  The problem with the algorithm I presented is not that it is using recursion, but that the complexity is 2n.  You need a much better algorithm than that.

I tried a basic iterative approach and started running into problems beyond a certain value due to the size of the numbers.  I certainly would not have been able to work out Fibonacci of one billion with my algorithm.

OK, won't you please post your first 10 bytes and the last 10 bytes of the number, plus the number of bits in the number, to verify that you indeed found it with your algorithm?

Looks like a memory allocation & GC benchmark... my ...

Looks like a memory allocation & GC benchmark... my hint: use a mutable BigInteger implementation, such as GNU Classpath's.

That might work.  I didn't use a mutable BigInteger for ...

That might work.  I didn't use a mutable BigInteger for my solution though.

Java Mind Gymnastics

Posted by kabutz on February 16, 2012 at 2:46 PM PST

I like a good Java puzzle.  The trickier the better.  In this article I will tell you about a new set of puzzles by Wouter Coekaerts that will melt your Java brain.

About five years ago, I was suckered into participating in a Java Black Belt competition at a Java Tech Days in London.  This was in the days before Her Royal Highness decided she had had enough South African visitors and thus closed the door for me by asking us to get visas.  Since I live on an island in the Mediterranean, acquiring a visa means flying to Athens and handing over my passport to the British Embassy for 6 weeks.  I travel at least once a month, so it will be a while before I can enjoy the warmth of English beer again.  The Java Black Belt competition consists of a bunch of questions, ranging from hard to very hard.  You have a limited time to answer them.  If you are lucky, you get given a set of relatively easy questions.  During the competition, I was lucky to get a high enough score, so that I got to the top of the scoreboard.  It was not a brilliant score by any means.  Olivier Croisier, my French instructor, scored a perfect 100% in the Sun Java Programmers Certification.  My score was probably about 95%.  Fortunately, someone recognized my name and told his buddies that they had no chance of dethroning me.  As a result, no one tried after me and I won a book called the Java Puzzlers.  I reviewed it on The Java Specialists' Newsletter.

Olivier Croisier publishes some excellent Java puzzles in his blog called "The Coder's Breakfast".  It is in French, but don't let that put you off.  Olivier does a very nice job of translating it to English for those of us who do not understand a word of French.  The name conjures up images of sitting in a Parisian cafe with a newspaper, watching the world go by whilst the butter is melting in the warm croissant and your cup of hot coffee is filling the room with a pleasant aroma.

And lastly, we have a hot new contender on the puzzle scene, Wouter Coekaerts.  He sent me some of his puzzles about a year ago and I've been begging him since then to please release them to the world.  They are funny and very challenging.  Definitely something for an advanced Java programmer.  You can use some dirty tricks, but nothing that would break the security manager.  Here is his first puzzle.  After a couple of weeks, Wouter will show the solution and his next puzzle.

Related Topics >>

Pushing the Limits in Java's Random

Posted by kabutz on February 14, 2012 at 3:46 AM PST

Welcome to the 198th issue of The Java(tm) Specialists' Newsletter sent to you from Chania in Greece. My readers in colder climates frequently get annoyed when I tell them that "life's a beach". They imagine us having constant sun here. It's not true. We do sometimes have torrential rains and cold weather. It even snows here occasionally. In fact, the delivery truck for my heating oil arrived as I was writing this.

This article was originally published in The Java Specialists' Newsletter.

Pushing the Limits in Java's Random

My previous newsletter caused quite a stir. I saw lots of wrong answers and some excellent explanations. Quite a few thought that Math.random() did indeed sometimes return 1.0. It does not, but the value is sometimes close enough to 1.0 that we lose precision in the rounding.

Here is what the nextDouble() calculation looks like in java.util.Random:

<b><font color="#000080">public</font></b> <b><font color="#000080">double</font></b> nextDouble() {
  <b><font color="#000080">return</font></b> (((<b><font color="#000080">long</font></b>)(next(<font color="#0000ff">26</font>)) &lt;&lt; <font color="#0000ff">27</font>) + next(<font color="#0000ff">27</font>))
    / (<b><font color="#000080">double</font></b>)(<font color="#0000ff">1L</font> &lt;&lt; <font color="#0000ff">53</font>);
}
 

One of our most thorough readers, Dr. Wolfgang Laun, went on a voyage of discovery to find out what the highest number was that could possibly be returned by Math.random(). This newsletter is a summary of his findings, plus some other tidbits that I discovered. Dr. Laun lives in Vienna in Austria and works for Thales as their local software guru. I've had the pleasure of meeting him personally and been invited for dinner at his house. He is probably the most intelligent human being I have met. And he also has a great sense of humour and a keen curiosity of how the world works. I am honoured to count him as a friend.

The first question that Wolfgang tried to answer is: Can (int)(Math.random() + 1) ever return 2? I thought it could. The calculation for producing the double is to make up a 53 bit number and to then divide it by 2^53. This means that the maximum possible number would be ((2^53)-1)/(2^53), or 0.9999999999999999. You can calculate this easily:

System.out.println((((<font color="#0000ff">1L</font> &lt;&lt; <font color="#0000ff">53</font>) - <font color="#0000ff">1</font>)) / (<b><font color="#000080">double</font></b>) (<font color="#0000ff">1L</font> &lt;&lt; <font color="#0000ff">53</font>));
 

If we use the standard Random calculation as shown in makeDouble(), if we add 1 and cast it to an int, the result will be 2. For example:

<b><font color="#000080">public</font></b> <b><font color="#000080">class</font></b> CloseToOne {
  <b><font color="#000080">public</font></b> <b><font color="#000080">static</font></b> <b><font color="#000080">double</font></b> makeDouble(<b><font color="#000080">long</font></b> first, <b><font color="#000080">long</font></b> second) {
    <b><font color="#000080">return</font></b> ((first &lt;&lt; <font color="#0000ff">27</font>) + second) / (<b><font color="#000080">double</font></b>) (<font color="#0000ff">1L</font> &lt;&lt; <font color="#0000ff">53</font>);
  }

  <b><font color="#000080">public</font></b> <b><font color="#000080">static</font></b> <b><font color="#000080">void</font></b> main(String[] args) {
    <b><font color="#000080">long</font></b> first = (<font color="#0000ff">1</font> &lt;&lt; <font color="#0000ff">26</font>) - <font color="#0000ff">1</font>;
    <b><font color="#000080">long</font></b> second = (<font color="#0000ff">1</font> &lt;&lt; <font color="#0000ff">27</font>) - <font color="#0000ff">1</font>;

    System.out.println(makeDouble(first, second));
    System.out.println((int)(makeDouble(first, second)+<font color="#0000ff">1</font>));

    second--;
    System.out.println(makeDouble(first, second));
    System.out.println((int)(makeDouble(first, second)+<font color="#0000ff">1</font>));
  }
}
 

In our previous newsletter, "meaning" was sometimes incremented by two, when Math.random was close enough to 1 and "meaning" was large enough. For example, if "meaning" is 100_000_000, we don't need to be as close to 1 as if "meaning" is 1.

<b><font color="#000080">public</font></b> <b><font color="#000080">class</font></b> CloseToOne2 {
  <b><font color="#000080">public</font></b> <b><font color="#000080">static</font></b> <b><font color="#000080">void</font></b> main(String[] args) {
    <b><font color="#000080">double</font></b> d = <font color="#0000ff">0.999999993</font>;
    System.out.println(<b><font color="#E1691A">&quot;d = &quot;</font></b> + d);
    <b><font color="#000080">int</font></b> i = (int) (<font color="#0000ff">1</font> + d);
    System.out.println(<b><font color="#E1691A">&quot;i = &quot;</font></b> + i);
    <b><font color="#000080">int</font></b> j = (int) (<font color="#0000ff">100_000_000</font> + d);
    System.out.println(<b><font color="#E1691A">&quot;j = &quot;</font></b> + j);
  }
}
 

The question is, can Math.random() return a significantly large number that is less than 1.0, but that makes (int)(Math.random() + 1) return 2? I thought so, but Wolfgang proved me wrong.

Java's random calculation is based on the 48-bit seed, linear congruential formula described in The Art of Computer Programming, Volume 2 by Donald Knuth in Section 3.2.1. This is also described on Wikipedia.

The point to note is that it is based on a 48-bit seed. In order for (int)(Math.random() + 1) to be 2, all the bits would have to be set on the left. If it is just one less, then the double would equal <font color="#0000ff">0.9999999999999998</font> and this would not round up to 2.

Since the next random value is based on the current value, all we would need to check is whether there exists a 48-bit number with the lower 26 bits set, such that the next number has the lower 27 bits set. If we find such a number, then we can conclude that at least in theory, it is possible for Math.random() to return <font color="#0000ff">0.9999999999999999</font>. We would still need to establish for what random seed we could get this number. If we did not find such a number, then we could conclude that it was not possible.

Wolfgang wrote the code to calculate the largest number that could be returned by Math.random():

<i><font color="808080">/**  * @author Wolfgang Laun  */</font></i>

<b><font color="#000080">public</font></b> <b><font color="#000080">class</font></b> FindRandom {
  <b><font color="#000080">private</font></b> <b><font color="#000080">final</font></b> <b><font color="#000080">static</font></b> <b><font color="#000080">long</font></b> multiplier = <font color="#0000ff">0x5DEECE66DL</font>;
  <b><font color="#000080">private</font></b> <b><font color="#000080">final</font></b> <b><font color="#000080">static</font></b> <b><font color="#000080">long</font></b> addend = <font color="#0000ff">0xBL</font>;
  <b><font color="#000080">private</font></b> <b><font color="#000080">final</font></b> <b><font color="#000080">static</font></b> <b><font color="#000080">long</font></b> mask = (<font color="#0000ff">1L</font> &lt;&lt; <font color="#0000ff">48</font>) - <font color="#0000ff">1</font>;

  <b><font color="#000080">public</font></b> <b><font color="#000080">static</font></b> <b><font color="#000080">double</font></b> makeDouble(<b><font color="#000080">long</font></b> pre, <b><font color="#000080">long</font></b> post) {
    <b><font color="#000080">return</font></b> ((pre &lt;&lt; <font color="#0000ff">27</font>) + post) / (<b><font color="#000080">double</font></b>) (<font color="#0000ff">1L</font> &lt;&lt; <font color="#0000ff">53</font>);
  }

  <b><font color="#000080">private</font></b> <b><font color="#000080">static</font></b> <b><font color="#000080">long</font></b> setbits(<b><font color="#000080">int</font></b> len, <b><font color="#000080">int</font></b> off) {
    <b><font color="#000080">long</font></b> res = (<font color="#0000ff">1L</font> &lt;&lt; len) - <font color="#0000ff">1</font>;
    <b><font color="#000080">return</font></b> res &lt;&lt; off;
  }

  <i><font color="808080">/**     * A random double is composed from two successive random     * integers.     * The most significant 26 bits of the first one are taken and     * concatenated with the most significant 27 bits of the second     * one.     * (ms(ri1,26)) &lt;&lt; 27 + (ms(ri2,27))     * This is divided by (double)(1L &lt;&lt; 53) to obtain a     * result in [0.0, 1.0).     * To find the maximum random double, we assume that     * (ms(ri1,m26))     * is a maximum (all 1b) and vary the remaining 22 bits from     * 0 to m22, inclusive. Assuming this to be ri1, we perform the     * calculation according to Random.next() to obtain is     * successor, our ri2. The maximum of the most significant 27     * bits of all ri2 would then be the second part of the maximum     * 53-bit integer used for a double random's mantissa.    */</font></i>
  <b><font color="#000080">private</font></b> <b><font color="#000080">static</font></b> <b><font color="#000080">void</font></b> findMaxDouble() {
    <b><font color="#000080">long</font></b> ones = setbits(<font color="#0000ff">26</font>, <font color="#0000ff">22</font>);
    System.out.println(<b><font color="#E1691A">&quot;ones: &quot;</font></b> + Long.toHexString(ones));
    <b><font color="#000080">long</font></b> maxpost = setbits(<font color="#0000ff">22</font>, <font color="#0000ff">0</font>);
    System.out.println(<b><font color="#E1691A">&quot;maxpost: &quot;</font></b> + Long.toHexString(maxpost));
    <b><font color="#000080">long</font></b> maxintw = <font color="#0000ff">0</font>;
    <b><font color="#000080">for</font></b> (<b><font color="#000080">long</font></b> post = <font color="#0000ff">0</font>; post &lt;= maxpost; post++) {
      <b><font color="#000080">long</font></b> oldseed = ones + post;
      <b><font color="#000080">long</font></b> nextseed = (oldseed * multiplier + addend) &amp; mask;
      <b><font color="#000080">long</font></b> intw = nextseed &gt;&gt;&gt; (<font color="#0000ff">48</font> - <font color="#0000ff">27</font>);
      <b><font color="#000080">if</font></b> (intw &gt; maxintw) {
        maxintw = intw;
      }
    }
    System.out.println(<b><font color="#E1691A">&quot;maxintw: &quot;</font></b> + Long.toHexString(maxintw));
    <b><font color="#000080">long</font></b> b26 = setbits(<font color="#0000ff">26</font>, <font color="#0000ff">0</font>);
    System.out.println(<b><font color="#E1691A">&quot;b26: &quot;</font></b> + Long.toHexString(b26));
    <b><font color="#000080">double</font></b> d = makeDouble(b26, maxintw);
    System.out.println(<b><font color="#E1691A">&quot;max. double: &quot;</font></b> +
        Double.toHexString(d) + <b><font color="#E1691A">&quot; = &quot;</font></b> + d);
  }

  <b><font color="#000080">private</font></b> <b><font color="#000080">static</font></b> <b><font color="#000080">void</font></b> findMinDouble() {
    <b><font color="#000080">long</font></b> b26 = <font color="#0000ff">0L</font>;
    <b><font color="#000080">long</font></b> zeroes = <font color="#0000ff">0L</font>;
    <b><font color="#000080">long</font></b> maxpost = setbits(<font color="#0000ff">22</font>, <font color="#0000ff">0</font>);
    <b><font color="#000080">long</font></b> minintw = <font color="#0000ff">0x7fffffffffffffffL</font>;
    <b><font color="#000080">for</font></b> (<b><font color="#000080">int</font></b> post = <font color="#0000ff">0</font>; post &lt; maxpost; post++) {
      <b><font color="#000080">long</font></b> oldseed = zeroes + post;
      <b><font color="#000080">long</font></b> nextseed = (oldseed * multiplier + addend) &amp; mask;
      <b><font color="#000080">long</font></b> intw = nextseed &gt;&gt;&gt; (<font color="#0000ff">48</font> - <font color="#0000ff">27</font>);
      <b><font color="#000080">if</font></b> (intw &lt; minintw) {
        minintw = intw;
      }
    }
    System.out.println(<b><font color="#E1691A">&quot;minintw: &quot;</font></b> + minintw);
    <b><font color="#000080">double</font></b> d = makeDouble(b26, minintw);
    System.out.println(<b><font color="#E1691A">&quot;min. double: &quot;</font></b> +
        Double.toHexString(d) + <b><font color="#E1691A">&quot; = &quot;</font></b> + d);
  }

  <b><font color="#000080">public</font></b> <b><font color="#000080">static</font></b> <b><font color="#000080">void</font></b> main(String[] args) {
    findMaxDouble();
    <b><font color="#000080">double</font></b> belowOne = Math.nextAfter(<font color="#0000ff">1.0</font>, <font color="#0000ff">0.0</font>);
    System.out.println(<b><font color="#E1691A">&quot;Biggest double below 1.0 is: &quot;</font></b> +
        Double.toHexString(belowOne) + <b><font color="#E1691A">&quot; = &quot;</font></b> + belowOne);
    findMinDouble();
  }
}
 

From this, we can calculate that the highest number that can be returned by Math.random() is 0.999999999999996, whereas the highest double less than 1.0 is 0.9999999999999999, a difference of .0000000000000039.

Concurrency

We could stop here, were it not for concurrency. However, the Math.random() method delegates to a shared mutable instance of Random. Since they use atomics, rather than locking, it is impossible for the compound nextDouble() method to be atomic. Thus it is possible that in between the two calls to next(27) and next(26), other threads call next(). Remember the code for Random.nextDouble() from earlier:

<b><font color="#000080">public</font></b> <b><font color="#000080">double</font></b> nextDouble() {
  <b><font color="#000080">return</font></b> (((<b><font color="#000080">long</font></b>)(next(<font color="#0000ff">26</font>)) &lt;&lt; <font color="#0000ff">27</font>) + next(<font color="#0000ff">27</font>))
    / (<b><font color="#000080">double</font></b>)(<font color="#0000ff">1L</font> &lt;&lt; <font color="#0000ff">53</font>);
}
 

In theory, if you had thousands of threads calling Math.random() at the same time, your thread could be swapped out for long enough, so that (int)(Math.random() + 1) could return 2. The probability of this happening might be greater than zero, but it is infinitesimally small. For example, in my experiments I did find a value of next(26) for which, if it was swapped out long enough and other threads had called next(27) 105 times, next(27) would return a number with all bits set. As Wolfgang told me, I'm rapidly entering into the realms of the metaphysical here.

Java 7 ThreadLocalRandom

As of Java 7, we should never again use Math.random(). Java 7 gives us a new ThreadLocalRandom class that has one unshared Random instance per thread. Since it is an unshared object, they do not need to have any synchronization to prevent data races. As a result it is blazingly fast.

We can use it like this: ThreadLocalRandom.current().nextInt(<font color="#0000ff">3000</font>);

In this example, we construct 20 buttons and let them change color at a random interval using ThreadLocalRandom. I will go over this code again in another newsletter, as it also demonstrates the new Java 7 Phaser class.

<b><font color="#000080">import</font></b> javax.swing.*;
<b><font color="#000080">import</font></b> java.awt.*;
<b><font color="#000080">import</font></b> java.util.*;
<b><font color="#000080">import</font></b> java.util.concurrent.*;

<i><font color="808080">/**  * @author Heinz Kabutz  */</font></i>
<b><font color="#000080">public</font></b> <b><font color="#000080">class</font></b> Blinker <b><font color="#000080">extends</font></b> JFrame {
  <b><font color="#000080">public</font></b> Blinker() {
    setLayout(<b><font color="#000080">new</font></b> GridLayout(<font color="#0000ff">0</font>, <font color="#0000ff">4</font>));
  }

  <b><font color="#000080">private</font></b> <b><font color="#000080">void</font></b> addButtons(<b><font color="#000080">int</font></b> buttons, <b><font color="#000080">final</font></b> <b><font color="#000080">int</font></b> blinks) {
    <b><font color="#000080">final</font></b> Phaser phaser = <b><font color="#000080">new</font></b> Phaser(buttons) {
      <b><font color="#000080">protected</font></b> <b><font color="#000080">boolean</font></b> onAdvance(<b><font color="#000080">int</font></b> phase, <b><font color="#000080">int</font></b> parties) {
        <b><font color="#000080">return</font></b> phase &gt;= blinks - <font color="#0000ff">1</font> || parties == <font color="#0000ff">0</font>;
      }
    };
    <b><font color="#000080">for</font></b> (<b><font color="#000080">int</font></b> i = <font color="#0000ff">0</font>; i &lt; buttons; i++) {
      <b><font color="#000080">final</font></b> JComponent comp = <b><font color="#000080">new</font></b> JButton(<b><font color="#E1691A">&quot;Button &quot;</font></b> + i);
      comp.setOpaque(<b><font color="#000080">true</font></b>);
      <b><font color="#000080">final</font></b> Color defaultColor = comp.getBackground();
      changeColor(comp, defaultColor);
      add(comp);
      <b><font color="#000080">new</font></b> Thread() {
        <b><font color="#000080">public</font></b> <b><font color="#000080">void</font></b> run() {
          Random rand = ThreadLocalRandom.current();
          <b><font color="#000080">try</font></b> {
            Thread.sleep(<font color="#0000ff">1000</font>);
            <b><font color="#000080">do</font></b> {
              Color newColor = <b><font color="#000080">new</font></b> Color(rand.nextInt());
              changeColor(comp, newColor);
              Thread.sleep(<font color="#0000ff">500</font> + rand.nextInt(<font color="#0000ff">3000</font>));
              changeColor(comp, defaultColor);
              Toolkit.getDefaultToolkit().beep();
              Thread.sleep(<font color="#0000ff">2000</font>);
              phaser.arriveAndAwaitAdvance();
            } <b><font color="#000080">while</font></b> (!phaser.isTerminated());
          } <b><font color="#000080">catch</font></b> (InterruptedException e) {
            Thread.currentThread().interrupt();
          }
        }
      }.start();
    }
  }

  <b><font color="#000080">private</font></b> <b><font color="#000080">void</font></b> changeColor(
      <b><font color="#000080">final</font></b> JComponent comp, <b><font color="#000080">final</font></b> Color color) {
    SwingUtilities.invokeLater(<b><font color="#000080">new</font></b> Runnable() {
      <b><font color="#000080">public</font></b> <b><font color="#000080">void</font></b> run() {
        comp.setBackground(color);
        invalidate();
        repaint();
      }
    });
  }

  <b><font color="#000080">public</font></b> <b><font color="#000080">static</font></b> <b><font color="#000080">void</font></b> main(String[] args) {
    SwingUtilities.invokeLater(<b><font color="#000080">new</font></b> Runnable() {
      <b><font color="#000080">public</font></b> <b><font color="#000080">void</font></b> run() {
        Blinker blinker = <b><font color="#000080">new</font></b> Blinker();
        blinker.addButtons(<font color="#0000ff">20</font>, <font color="#0000ff">3</font>);
        blinker.pack();
        blinker.setVisible(<b><font color="#000080">true</font></b>);
        blinker.setDefaultCloseOperation(
            WindowConstants.EXIT_ON_CLOSE);
      }
    });
  }
}
 

If you run the Blinker with a version of Java 7 older than 1.7.0_02, you will notice that all the colors and delays are always the same. This is due to a bug in the constructor of java.util.Random, which failed to set the seed correctly on the subclass. Time to upgrade to the latest version of Java 7. Note that if you are using Mac OS X Snow Leopard or Leopard, the Mac OS X Port installer will tell you that you need to upgrade to Lion. Instead, I used Pacifist to extract the DMG file. I am not ready to install Lion on my work machines yet.

(int)(Math.random() * some_int)

A common code idiom is to multiply the result of Math.random() with some integer value. There are several reasons not to do that anymore. First off, Math.random() is using a shared mutable instance of Random, thus it needs to be synchronized. It does use atomics to eliminate locking, but with lots of threads calling Math.random(), you might still need to redo your work several times until you emerge as the winner.

The second reason is that Math.random() calls nextDouble(), which as we have seen, will call next(bits) twice.

The third reason is that nextDouble() * int is more biased. We will get a fairer distribution if we call nextInt(upto).

One Last Thing

Concurrency allows us to do some strange things. For example, we can set the seed on a shared mutable Random instance so that eventually, (int)(random.nextDouble() + 1) will return 2. Here is the code:

<b><font color="#000080">public</font></b> <b><font color="#000080">class</font></b> MathTeaser {
  <b><font color="#000080">public</font></b> <b><font color="#000080">static</font></b> <b><font color="#000080">void</font></b> main(String[] args) {
    <b><font color="#000080">final</font></b> Random random = <b><font color="#000080">new</font></b> Random();
    Thread seeder = <b><font color="#000080">new</font></b> Thread() {
      <b><font color="#000080">public</font></b> <b><font color="#000080">void</font></b> run() {
        <b><font color="#000080">while</font></b>(<b><font color="#000080">true</font></b>) {
          random.setSeed(<font color="#0000ff">51102269</font>); <i><font color="808080">// causes 2^26-1 as next(26)</font></i>
          random.setSeed(<font color="#0000ff">223209395</font>); <i><font color="808080">// causes 2^27-1 as next(27)</font></i>
        }
      }
    };
    seeder.setDaemon(<b><font color="#000080">true</font></b>);
    seeder.start();

    <b><font color="#000080">while</font></b>(<b><font color="#000080">true</font></b>) {
      <b><font color="#000080">double</font></b> num = random.nextDouble();
      <b><font color="#000080">if</font></b> ((<b><font color="#000080">int</font></b>)(num + <font color="#0000ff">1</font>) == <font color="#0000ff">2</font>) {
        System.out.println(<b><font color="#E1691A">&quot;Yes, random.nextDouble() can: &quot;</font></b> + num);
        <b><font color="#000080">break</font></b>;
      }
    }
  }
}
 

Thanks to Dr. Wolfgang Laun for the fascinating discussions of how Random really works and for your contribution in this newsletter.

Kind regards

Heinz

What is the meaning of life?

Posted by kabutz on December 21, 2011 at 11:37 PM PST

A few weeks ago I updated my age to be a factor of 2 and 5.  It is the perfect age to reflect what life is all about.  Some men don a leather jacket and ride around on a Harley.  But as a geek I know exactly where to turn - my beloved computer.  

I needed a long-running method for the new concurrency course I am writing.  Something that would take about 15 seconds and that would keep the CPU busy at 100%.  Also, since life throws us random events, we would have to include a call to Math.random() in the calculation.  In my next article I will explain why Math.random() is dead, long live Java 7, but for now we will call it in order to be super slow.

<strong>public class</strong> MeaningOfLife {
  <strong>public static</strong> String findOutWhatLifeIsAllAbout() {
    <strong>int</strong> meaning = 0;
    <strong>for</strong> (<strong>int</strong> i = 0; i &lt; 10; i++) {
      <strong>for</strong> (<strong>int</strong> j = 0; j &lt; 20; j++) {
        <strong>for</strong> (<strong>int</strong> k = 0; k &lt; 300; k++) {
          <strong>for</strong> (<strong>int</strong> m = 0; m &lt; 7000; m++) {
            meaning += Math.random() + 1;
          }
        }
      }
    }
&nbsp;&nbsp;&nbsp; <strong>return</strong> String.valueOf(meaning).replaceAll(&quot;0*$&quot;, &quot;&quot;);
&nbsp; }

&nbsp; <strong>public static void</strong> main(String[] args) {
&nbsp;&nbsp;&nbsp; System.out.println(findOutWhatLifeIsAllAbout());
&nbsp; }
}

Think about what the output should be before you run it. Then try it out, preferably with the -server switch. On my machine it takes 15 seconds to find the meaning of life with -server and 25 seconds with -client. Patience is apparently a virtue, though I would not be able to confirm or deny that.  Never had patience, which is why I always want the fastest meanest computer that I can get.

The question is: Why is it giving this result? And why does it even compile?
 

 

Comments

Please don't post spoilers until after Christmas - let your ...

Please don't post spoilers until after Christmas - let your fellow Java experts think a bit about what the answer would be first :-)

public static void main(String args) { test(1); ...

public static void main(String[] args)  {
    test(1);
    test(167390728);
}

static void test(int intVal) {
    {
        int v1 = intVal;
        double v2 = 0.9999999945056177;
        double sum = v1 + v2;
        System.out.println(v1 + &quot; +  &quot; + v2 + &quot; = &quot; + sum + &quot; aka &quot; + new BigDecimal(sum) + &quot; =(int) &quot; + (int) sum);
    }
    {
        double v1 = intVal;
        double v2 = 0.9999999945056177;
        double sum = v1 + v2;
        System.out.println(v1 + &quot; +  &quot; + v2 + &quot; = &quot; + sum + &quot; aka &quot; + new BigDecimal(sum) + &quot; =(int) &quot; + (int) sum);
    }
}

Output:
1 + 0.9999999945056177 = 1.9999999945056177 aka 1.999999994505617717521772647160105407238006591796875 =(int) 1
1.0 + 0.9999999945056177 = 1.9999999945056177 aka 1.999999994505617717521772647160105407238006591796875 =(int) 1
167390728 + 0.9999999945056177 = 1.67390729E8 aka 167390729 =(int) 167390729
1.67390728E8 + 0.9999999945056177 = 1.67390729E8 aka 167390729 =(int) 167390729
First two lines truncate as expected, second two appear rounded as seen. (A '+= ' does the same, as seen previously).

Further, here's the output from a C++ program doing the same:
1 + 0.99999999450561771752 = 1.99999999450561771752 =(int) 1
1.00000000000000000000 + 0.99999999450561771752 = 1.99999999450561771752 =(int) 1
167390728 + 0.99999999450561771752 = 167390729.00000000000000000000 =(int) 167390729
167390728.00000000000000000000 + 0.99999999450561771752 = 167390729.00000000000000000000 =(int) 167390729
It appears that the issue has nothing to do with the += operation, nor int values, nor java ... but rather floating point processing (ie. hardware/standards).
Though the 'int += double' allowance is interesting... ;)

Multiple Layers I think there are multiple facets to this ...

Multiple Layers

I think there are multiple facets to this puzzle, and all have been touched on by different responders. I think I can help make some sense of this puzzle.

We start with yishai who uncovered the value of Math.random() that causes the anomaly. In my experiment, I modified the code (inside the deepest for loop) by...

// adding extra variables
double rand = Math.random();      // we save Math.random() into a variable for later output
double rand_plus_1 = rand + 1;    // I just wanted to see what would happen
int xmeaning = meaning;           // we use this for comparison
int temp = (int)(rand + 1);       // always equal to 1, strange...

// modifying existing code
meaning += rand + 1;              // meaning += Math.random() + 1;   &lt;-- old code

// and finding and displaying the anomaly
if(meaning - xmeaning != 1) {
   System.out.println(xmeaning + &quot;-&gt;&quot; + meaning + &quot; &quot; + rand + &quot; &quot; + rand_plus_1);
}

The output of the program is...

167390728-&gt;167390730 0.9999999945056177 1.9999999945056177
205622567-&gt;205622569 0.999999996646961  1.999999996646961
245591182-&gt;245591184 0.9999999863337321 1.999999986333732
339981965-&gt;339981967 0.9999999905665823 1.9999999905665824
368881211-&gt;368881213 0.9999999843595886 1.9999999843595886
378638703-&gt;378638705 0.9999999776003651 1.999999977600365
420000006

As you can see, random values greater than approximately 0.99999997 cause the anomaly. Thank you yishai.

The Anomaly

So what is the anomaly exactly? The post by forax about compound operators combined with yishai's discovery (above) is the answer to that question.

Observe... Look at the output above, look only at the first line. As you can see, meaning incremented by 2 in the first column. Math.random() generated the value in the second column, and that value plus one is the value in the third column... the increment value! It is not quite 2.

SO, IF THE INCREMENT VALUE IS NOT QUITE 2, HOW DID IT INCREMENT BY 2!?

After all, if you cast this value (the one from the third column) to an int, all of the remainder is stripped and all you're left with is 1. This is (in theory) how it should have worked, however, we have not cast anything to an int! The code...

meaning += rand + 1;

simply stuffs a double (incremented by 1 but still a double) into an int in order to increment some other int. This stuffing operation is NOT the same as a casting operation. But if we assume they are the same, which most programmers will, we are making a wrong assumption. Whether the increment operator or something else inside the virtual machine is broken, all I can say is forax lead us in the right direction.

So, the way we fix our code to get that nice "42" output is to actually cast the random number first to an int. Like so...

meaning += (int)(rand + 1);

Problem solved.

Final Note

I want to clarify that while it is possible (albeit inaccurate, as we have proven) to stuff a double into an int during a compound operation, it is not possible to do so during an assignment operation. But we already knew this.

some_int += some_double + 1;  // possibly buggy, however this operation is legal
some_int = some_double + 1;   // compiler error

In other words, I cannot answer the author's original question of "How is it even able to compile?" I do wonder, though, if any documentation exists which could have alerted a studious programmer to this anomaly i.e. virtual machine specifications, java language specifications, etc.

You peeled away all the layers like from an onion.&nbsp; ...

You peeled away all the layers like from an onion.  Lovely, thanks for your great explanation.

Since it is after Christmas ... meaning += Math.random() ...

Since it is after Christmas ...

meaning += Math.random() + 1;

can be thought of as:

double doubleMeaning = meaning + Math.random();

meaning = (int) doubleMeaning + 1;

For some values of meaning and Math.random() the resulting double will be represented as a whole number. Consider this case:

System.out.println(BigDecimal.valueOf(280658056.9999999876849885));

So if the value in the loop was 280658056 and the value of Math.random() returned 0.9999999876849885, then the result will already increment meaning by 1, causing that pass of the loop to increase it by two. So the number above 420 million indicates how many times that condition was met.

Yes. &nbsp;It should also be pointed out that for ...

Yes.  It should also be pointed out that for Math.random(), this can only happen with a large value of "meaning".  It is impossible in the current Math.random() implementation for (int)(Math.random() + 1) to equal 2.  Anybody like to try figure out why?

Wait a second, I definitely missed something then.&nbsp; I ...

Wait a second, I definitely missed something then. I thought it was strictly the size of the random number, but what you're saying is that the size of "meaning" also matters. Well, I just ran other tests to see if I could qualify this. They capture all anomalies AND all high random numbers. However, these tests appear to be inconclusive. In one test, the code...

if(meaning - xmeaning != 1 || rand &gt; 0.99999997) {
    System.out.println(xmeaning + &quot;-&gt;&quot; + meaning + &quot; &quot; + rand + &quot; &quot; + rand_plus_1);
}

produced...

24371067-&gt;24371068   0.9999999828062357 1.9999999828062358
59006142-&gt;59006143   0.9999999757290122 1.9999999757290121
85837282-&gt;85837283   0.9999999807169675 1.9999999807169675
107546119-&gt;107546121 0.9999999971733692 1.999999997173369  (*)
117495561-&gt;117495562 0.9999999729988222 1.9999999729988223
123249024-&gt;123249026 0.9999999965744986 1.9999999965744986 (*)
140167680-&gt;140167681 0.9999999809920106 1.9999999809920106
145594763-&gt;145594765 0.9999999996361264 1.9999999996361264 (*)
160475235-&gt;160475236 0.9999999793442659 1.999999979344266
178166317-&gt;178166318 0.9999999760638756 1.9999999760638756
196935260-&gt;196935262 0.9999999999024445 1.9999999999024445 (*)
217866433-&gt;217866434 0.9999999707050973 1.9999999707050973
239191478-&gt;239191479 0.9999999749421629 1.9999999749421629
283879355-&gt;283879357 0.9999999707268747 1.9999999707268747 (*)
333059249-&gt;333059251 0.9999999792976765 1.9999999792976766 (*)
420000006

In lines 1 - 3 "meaning" is small and there are no anomalies, so perhaps you're right. But there are some qualifying combinations that don't result in anomalies, how can those be explained? By the way there is an answer to this riddle, right? When do we get to know what it is?

Compound operator is broken, short x = 3; x += 4.6; is ...

Compound operator is broken, [1]

short x = 3;
x += 4.6;

is equivalent to

short x = 3;
x = (short)(x + 4.6);

and
  Math.random() returns a value between [0, 1[, so the value can be ignored.

Rémi

[1] http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#5304

Thus, we would expect the value to meaning to be ...

Thus, we would expect the value to meaning to be 420,000,000, then we truncate the zeros with the regular expression and we end up with 42?  After all, Math.random() never returns 1.0 :-)

&nbsp;We would thusly, except I'm returned 420000006 (or ...

We would thusly, except I'm returned 420000006 (or same value with different trailing int).

I would reflexively assume that Math.random() DOES return 1.0 on the odd occasion regardless of the obviously lying documention, but I suspect I'm overlooking something trite.

Nope, Math.random() for sure never returns 1.0 :-)

Nope, Math.random() for sure never returns 1.0 :-)

&nbsp;In any case, I'm quite interested in understanding why ...

In any case, I'm quite interested in understanding why I'm NOT returned 420,000 then truncated to 42. Which of course is the answer to life, the universe and everything.

Right, but the best puzzles are those that we eventually ...

Right, but the best puzzles are those that we eventually figure out :-)  Math.random() never returns 1.0.  It is always less than 1.0.

Try figure out for what values of "meaning" and random it increments by 2, then you will have the answer (almost) figured out :-)

Interresting, when I've answered I was on my phone, so no ...

Interresting, when I've answered I was on my phone, so no way to actually run the program.
I think I've found the problem, as often the computer is right, the error is that we don't have the same meaning for +.

The + between math.random() and 1 is a + between a double and an int, so the int is converted
to a double, so here 1 is converted to 1.0 which is in fact 1.0000000000000XXX so
math.random() is between [0, 1[ but math.random() + 1 can be >= 2.0

I hate IEEE 754 :)

Rémi
 

Hum, reading the other comments before posting a comment is ...

Hum, reading the other comments before posting a comment is a good idea. jaeksmith has already provided the anwser, yesterday. Remi