简体   繁体   中英

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 :

  • Math.pow(x,y) is slower than Math.Exp(Math.log(x) * y)

For eg:

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 ?

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.

Math.Pow必须进行一些额外的检查,以便在x < 0toPower为整数时返回正确的值。

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM