Dio Phung
Dio Phung

Reputation: 6282

An efficient way to implement power function: Why Math.Exp(x * Math.Log(n)) is faster than Math.Pow()?

I'm trying to solve this problem in C# using these 2 approaches:

public double NormalPowerMethod(double x, double toPower)
{
    return Math.Pow(x, toPower);
}

public double EfficientPowerMethod(double x, double toPower)
{
    return Math.Exp(Math.Log(x) * toPower);
}

I ran a simple test program :

EfficientPower ep = new EfficientPower();
Console.WriteLine("Enter x:");
double x = Double.Parse(Console.ReadLine());
Console.WriteLine("Enter power: ");
int toPower = Int32.Parse(Console.ReadLine());
Stopwatch timer = new Stopwatch();
timer.Start();
Console.WriteLine(string.Format("{0} to the power of {1}= {2}", x,toPower, ep.NormalPowerMethod(x,toPower)));
timer.Stop();
Console.WriteLine("Normal power time =" + timer.ElapsedTicks);

//-------------- efficient
timer.Reset();
timer.Start();
Console.WriteLine(string.Format("Efficient power: {0} to the power of {1}= {2}", x, toPower, ep.EfficientPowerMethod(x, toPower)));
timer.Stop();
Console.WriteLine("Efficient power time =" + timer.ElapsedTicks);

Console.ReadLine();

and I noticed :

For e.g:

Enter x:
15
Enter power:
26
Normal power: 15 to the power of 26= 3.78767524410635E+30
Normal power time =818
Efficient power: 15 to the power of 26= 3.78767524410634E+30
Efficient power time =533


Enter x:
1024
Enter power:
1024
Normal power: 1024 to the power of 1024= Infinity
Normal power time =810
Efficient power: 1024 to the power of 1024= Infinity
Efficient power time =519

Why such behaviour ? I thought more calculation will be slower ?

Upvotes: 4

Views: 1420

Answers (3)

David Heffernan
David Heffernan

Reputation: 613302

Hans's answer here (How is Math.Pow() implemented in .NET Framework?) explains that Math.Pow is not implemented in terms of Exp and Log. The implementation of Math.Pow(), once it has performed domain validation, calls the unmanaged CRT function pow(). It is not done that way, I presume, because the CRT pow() is more accurate than the Exp/Log expression.

So, the reason that Math.Pow is slower than the Exp/Log variant is that the former does a different calculation that takes longer. And the implementors chose that different calculation because it is more accurate than the Exp/Log version. In essence they preferred accuracy to efficiency.

Upvotes: 2

Patrick Hofman
Patrick Hofman

Reputation: 157048

Here is the correct measurement script, since we already discusses your measurement is off:

static void Main(string[] args)
{
    DateTime start = DateTime.Now;
    for (int i = 0; i < 10000000; i++)
    {
        Math.Pow(10, 100);
    }

    TimeSpan diff = DateTime.Now - start;
    Console.WriteLine("Normal: {0:N0}", diff.TotalMilliseconds);

    //-------------- efficient
    start = DateTime.Now;
    for (int i = 0; i < 10000000; i++)
    {
        Math.Exp(Math.Log(10) * 100);
    }

    diff = DateTime.Now - start;
    Console.WriteLine("Efficient: {0:N0}", diff.TotalMilliseconds);

    Console.ReadLine();
}

The output with 10.000.000 tries (statistics are consistent after numerous tries):

Normal:    1.968 ms.
Efficient: 1.813 ms.

So 'efficient' performs about 8% better.

About the why:

According to Hans Passants answer:

It basically checks for corner cases, then calls the CRT's version of pow().

So there is some, though minor overhead.

Upvotes: 3

Henrik
Henrik

Reputation: 23324

Math.Pow has to do a few additional checks so it can return correct value if x < 0 and toPower is an integer.

Upvotes: 2

Related Questions