C#Math.Pow()坏了

时间:2013-01-16 20:41:51

标签: .net math floating-point pow exponent

不,这不是(据我的理解)涉及整数除法或浮点舍入问题。

我的确切代码是:

    static void Main(string[] args)
    {
        double power = (double)1.0 / (double)7.0;
        double expBase = -128.0;
        System.Console.WriteLine("sanity check: expected: -128 ^ 0.142857142857143 = -2.    actual: " + expBase + " ^ " + power + " = " + Math.Pow(expBase, power));
        System.Console.ReadLine();
    }

输出结果为:

sanity check: expected: -128 ^ 0.142857142857143 = -2. actual: -128 ^ 0.14285 7142857143 = NaN

此代码的目标框架是(根据解决方案属性).NET Framework 4.0 Client Profile。

奇怪的是,我在网上的任何地方都没有提到这一点。我在这里吃疯了药吗??

6 个答案:

答案 0 :(得分:17)

似乎完全符合规定;来自Pow(x,y)的{​​{3}};

  

参数
  x< 0但不是NegativeInfinity; y不是整数,NegativeInfinity或PositiveInfinity。

     

结果
  为NaN

答案 1 :(得分:12)

Joachim 的回答解释说,pow的行为符合其规范。

为什么指定pow()?因为1.0/7.0不等于1/7。您要求0.14285714285714285的{​​{1}}幂,并且该属性没有实数,因此结果正确为NaN。对于所有奇数-128.0n != 1不能完全表示,因此您无法使用1.0/(double)n计算x的第n个根。因此,pow(x, 1.0/(double)n)指定为负x和非整数y返回NaN - 对于任何这些情况都没有适当的实际结果。

答案 2 :(得分:3)

这里的问题是“第七根”的数学定义是multivalued function。虽然确实如此

  

( - 2) 7 = -128

not 意味着-2是(-128) 1/7 的唯一答案。在复平面中,第七根函数是多值的并且有许多可能的答案(正如4的平方根可以被认为是+2或-2,但+2是通常的答案)。

为了简化这些表达式的数学处理,principal value按惯例选择用于所讨论的函数,以便函数变为单值。在第七根的情况下,主要值是由Wolfram Alpha for (-128)1/7给出的。

C#中的Math.Pow()函数尝试返回pow函数的主值。当结果的主值是一个复数时,它返回NaN

答案 3 :(得分:1)

负实数的分数幂是一个复数(详见[{3}})。

答案 4 :(得分:0)

我修复了Math.Pow()。

它现在有一个更大的接受域(即参数:x <0但不是NegativeInfinity; y是分子为1且奇数分母的分数),并返回新域区域的实数结果。

换句话说,(-128)^(1/7)返回-2。

注意:由于双浮点精度限制,它适用于大多数(但不是全部)小数指数。

下面是我写的Math.Pow()包装器的代码。

public class MathUtil
{
    /// <summary>
    /// Wrapper for Math.Pow()
    /// Can handle cases like (-8)^(1/3) or  (-1/64)^(1/3)
    /// </summary>
    public static double Pow(double expBase, double power)
    {
        bool sign = (expBase < 0);
        if (sign && HasEvenDenominator(power)) 
            return double.NaN;  //sqrt(-1) = i
        else
        {
            if (sign && HasOddDenominator(power))
                return -1 * Math.Pow(Math.Abs(expBase), power);
            else
                return Math.Pow(expBase, power);
        }
    }

    private static bool HasEvenDenominator(double input)
    {
        if(input == 0)
            return false;
        else if (input % 1 == 0)
            return false;

        double inverse = 1 / input;
        if (inverse % 2 < double.Epsilon)
            return true;
        else
            return false;
    }

    private static bool HasOddDenominator(double input)
    {
        if (input == 0)
            return false;
        else if (input % 1 == 0)
            return false;

        double inverse = 1 / input;
        if ((inverse + 1) % 2 < double.Epsilon)
            return true;
        else
            return false;
    }
}

答案 5 :(得分:0)

对于1.0 / 3!= 1/3,我使用可以在Microsoft.SolverFoundation.Common中准确表示1/3的Rational。参见:https://msdn.microsoft.com/en-us/library/microsoft.solverfoundation.common.rational%28v=vs.93%29.aspx?f=255&MSPPError=-2147217396

我可以捕捉奇数根链接1/3因为我可以获得分母。

如果我得到一个root是ax,我使用代码得到Numerator和Denominator。

            var at = (double)ax.Numerator;
            var down = (double)ax.Denominator;

Rational可以使2/6 = 1/3。

但Rational.Pow无法计算powerBase不是正面的。

我发现powerBase不是正数,分母是偶数,而分子是奇数。

             if (at % 2 == 1 && down % 2 == 0)
            {
                return Double.NaN;
            }

如果分母是奇数,我使用x = x * -1

            if (at % 2 == 1 && down % 2 == 1)
            {
                x = Math.Pow(x, (int)at);
                x = x * -1;
                return -1 * Math.Pow(x, 1.0 / (int)down);
            }

如果分子是偶数,则分子的功率使powerBase为正。

与pow(x,2/3)类似,如果x不是正数,则在使用pow(x,2)时它将是正数

            x = Math.Pow(x, (int)at);
            return Math.Pow(x, 1.0 / (int)down);

您可以使用的代码

       if (x < 0)
        {                
            var at = (double)ax.Numerator;
            var down = (double)ax.Denominator;

            if (at % 2 == 1 && down % 2 == 0)
            {
                return Double.NaN;
            }
            if (at % 2 == 1 && down % 2 == 1)
            {
                x = Math.Pow(x, (int)at);
                x = x * -1;
                return -1 * Math.Pow(x, 1.0 / (int)down);
            }
            x = Math.Pow(x, (int)at);
            return Math.Pow(x, 1.0 / (int)down);
        }
        else
        {
            return Math.Pow(x, a);
        }