2

When I run the code below the result is always different when it comes to the 5th and 6th digit:

public void TestParallel()
    {
        object locker = new object();
        double grandTotal = 0;
        Parallel.For( 1, 1000000,

            () => 0.0, // Initialize the local value.

            ( i, state, localTotal ) => // Body delegate. Notice that it
            {
                return localTotal + Math.Sqrt( i ); // returns the new local total.
            },

            localTotal => // Add the local value
            {
                lock( locker ) grandTotal += localTotal; // to the master value.
            }
            );
        Console.WriteLine( string.Format( "Grand Total = {0}", grandTotal ) );
    }

I have no clue why the result is not always Grand Total = 666666166,458842. This is what i get when running it like this:

double grandTotal = 0;
for ( int i = 1; i < 1000000; i++ )
{
    grandTotal += Math.Sqrt( i ); // returns the new local total.
}
Console.WriteLine( string.Format( "Grand Total = {0}", grandTotal ) );
amaters
  • 2,266
  • 2
  • 24
  • 44
  • 5
    floating point math is hard. – Daniel A. White Jul 06 '16 at 12:52
  • Also your altering a single variable using multiple threads. The order of how these are processed is going to alter – Liam Jul 06 '16 at 12:54
  • when I perform this task synchronous the result is the same everytime i run it – amaters Jul 06 '16 at 12:54
  • Run it synchonous then? If your trying to speed it up this isn't going to work – Liam Jul 06 '16 at 12:54
  • 2
    @Liam Note that the only shared variable he changes is grandTotal and he locks it before altering it. – Taemyr Jul 06 '16 at 12:55
  • Yes but which thread is locking when? if It runs 1, then 2 then the total is x if it runs 2 then 1 then the total is y. Basically you have a race condition. The lock nullifies the any benfit for the threads so simply don't thread it – Liam Jul 06 '16 at 12:56
  • @Liam I am not actually trying to speed the sqrt method up. Just trying to learn the TPL library capabilities – amaters Jul 06 '16 at 12:57
  • If your locking your not really using TPL. Only run threads that peform independant actions that don't update the same resource, should be threaded – Liam Jul 06 '16 at 12:57
  • 1
    good point @Liam. Sqrt functions are probably a single instruction on modern processors, which although may have more than 1 'virtual processor', they might not have multiple floating point units. In this particular case, it's unlikely Parallel.For is going to show any benefit (and could slow things down due to locking). – Neil Jul 06 '16 at 12:58
  • In fact the Parallel.For code is almost twice as fast but thats not the issue I am having problems with – amaters Jul 06 '16 at 13:00
  • @Neil It doesn't matter that sqrt is not a single instruction. The disrepancy comes from floating point rounding. – Taemyr Jul 06 '16 at 13:00
  • Twice as fast proibably using 3 times the resources – Liam Jul 06 '16 at 13:01
  • @Taemyr my comment was not about the result, but about the whole process. – Neil Jul 06 '16 at 13:01
  • 2
    Floating addition is not associative. Order of evolution matters. Compare `(-1000000 + 1000000) + 0.1` and `-1000000 + (1000000 + 0.1)`. – user4003407 Jul 06 '16 at 13:02

2 Answers2

2

You get inconsistent result because floating point arithmetic rounds the numbers. And that rounding depends on the number being rounded.

Hence (0+1)+sqrt(2) is not guaranteed to be equal to (0+sqrt(2))+1.

Taemyr
  • 3,407
  • 16
  • 26
2

With floating point calculations, 1.0 + 0.9 doesn't always equal 0.9 + 1.0

Neil
  • 11,059
  • 3
  • 31
  • 56