实现幂函数的有效方法:为什么Math.Exp(x * Math.Log(n))比Math.Pow()更快?

时间:2014-06-03 09:20:10

标签: c# math biginteger bigdecimal

我试图使用这两种方法在C#中解决这个问题:

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();

我注意到了:

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

例如:

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

为什么会出现这种行为?我认为更多的计算会更慢?

3 个答案:

答案 0 :(得分:2)

这是正确的测量脚本,因为我们已经讨论过您的测量结果:

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();
}

输出尝试10.000.000次(经过多次尝试后统计数据保持一致):

Normal:    1.968 ms.
Efficient: 1.813 ms.

所以'高效'的表现要好8%左右。

关于原因:

根据Hans Passants的回答:

  

它基本上检查了极端情况,然后调用CRT版本的pow()。

所以有一些,虽然是轻微的开销。

答案 1 :(得分:2)

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

答案 2 :(得分:2)

汉斯在这里的回答(How is Math.Pow() implemented in .NET Framework?)解释说Math.Pow没有按ExpLog实施。 Math.Pow()一旦执行了域验证,就会调用非托管CRT函数pow()。我认为,这不是那样做的,因为CRT pow()Exp/Log表达式更准确。

因此,Math.PowExp/Log变体慢的原因是前者执行的计算时间更长。并且实现者选择了不同的计算,因为它比Exp/Log版本更准确。从本质上讲,他们更倾向于提高效率。