Reputation: 6282
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);
}
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 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
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
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
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