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