C# Math.Pow() не работает

И нет, это (на мой взгляд) не связано с целочисленным делением или проблемами округления с плавающей точкой.

Мой точный код:

    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 ответов

Решение

Кажется, точно так, как указано; из раздела замечаний Math.Pow() на Pow(x,y);

параметры
x < 0, но не NegativeInfinity; y не является целым числом, NegativeInfinity или PositiveInfinity.

Результат
NaN

ОтветИоахима объясняет, что пау ведет себя в соответствии с его спецификацией.

Почему pow() указан именно так? Так как 1.0/7.0 не равно 1/7. Вы просите 0.14285714285714285 сила -128.0и не существует действительного числа с этим свойством, поэтому результат будет правильно NaN. Для всех странных n != 1, 1.0/(double)n не является точно представимым, поэтому вы не можете вычислить n-й корень из x, используя pow(x, 1.0/(double)n), Следовательно pow(x, y) указано возвращать NaN для отрицательного x и нецелого y - для любого из этих случаев нет подходящего реального результата.

Проблема здесь заключается в том, что математическое определение "седьмой корень" является многозначной функцией. Хотя это правда, что

(-2)7 = -128

это не означает, что -2 является единственным ответом на (-128)1/7. В комплексной плоскости функция седьмого корня является многозначной и имеет много возможных ответов (так же, как квадратный корень из 4 можно считать либо +2, либо -2, но +2 - это обычный ответ).

Чтобы упростить математическую обработку таких выражений, основное значение выбирается по соглашению для рассматриваемой функции, так что функция становится однозначной. В случае седьмого корня главное значение имеет значение, данное Wolfram Alpha для (-128)1/7.

Math.Pow() функция в C# пытается вернуть главное значение для pow функция. Когда основным значением результата будет комплексное число, возвращается NaN,

Дробная степень отрицательного действительного числа является комплексным числом (подробное объяснение см. На форуме по математике).

Я исправил 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;
    }
}

Для 1.0/3!= 1/3 я использую Rational, который может точно представлять 1/3 в Microsoft.SolverFoundation.Common . См. https://msdn.microsoft.com/en-us/library/microsoft.solverfoundation.common.rational%28v=vs.93%29.aspx?f=255&MSPPError=-2147217396

И я могу поймать нечетную корневую ссылку 1/3, потому что я могу получить Знаменатель.

Если я получаю рут-топор, то использую код, получаю Числитель и Знаменатель.

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

Рационально можно сделать 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);
        }
Другие вопросы по тегам