题
当我试图找到一个数字的立方根时,奇怪的事情发生了。
以下代码返回未定义的结果。在命令中:-1.#IND
cout<<pow(( double )(20.0*(-3.2) + 30.0),( double )1/3)
虽然这个工作得很好。在命令中:4.93242414866094
cout<<pow(( double )(20.0*4.5 + 30.0),( double )1/3)
从数学的角度来看,它一定有效,因为我们可以从负数求立方根。Pow 来自 Visual C++ 2010 math.h 库。有任何想法吗?
解决方案
pow(x, y)
从 <cmath>
如果 x 为负且 y 为非整数,则不起作用。
这是一个限制 std::pow
, ,如 C 标准和中所述 参考参数:
错误处理
- 按照 math_errhandling 中的指定报告错误
- 如果 base 是有限且负的,而 exp 是有限且非整数,则会出现域错误并且可能会出现范围错误。
- 如果 base 为零且 exp 为零,则可能会出现定义域错误。
- 如果基数为零且exp为负,则可能会出现域错误或极点错误。
有几种方法可以解决此限制:
立方求根与取某物的 1/3 次方相同,所以你可以这样做
std::pow(x, 1/3.)
.在 C++11 中,您可以使用
std::cbrt
. 。C++11 引入了平方根和立方根函数,但没有通用的 n 次根函数来克服以下限制:std::pow
.
其他提示
在功率1/3
是一种特殊情况。在一般情况下,负数的非整数次幂是复杂的。这将是不实际的战俘检查特殊情况下,像整数根,而且,1/3
为双不完全是1/3!
我不知道有关Visual C ++战俘,但在错误我的男人网页显示:
EDOM
的参数x
为负且y
不是一个整数值。这将导致一个复数。
您将有如果你想负数的立方根用更专业立方根功能 - 或者偷工减料,采取绝对值,再取立方根,然后乘上签回
请注意,根据上下文,负数x
到1/3
功率不一定你期望的负立方根。它可以很容易地成为第一个复杂的根源,x^(1/3) * e^(pi*i/3)
。这是惯例Mathematica使用;这也是合理的,只说这是不确定的。
虽然(-1)^ 3 = -1,则不能简单地采取负数的一个合理的功率,并期望一个真实的反应。这是因为有这种理性的指数是在自然界中假想的其他解决方案。结果 HTTP:/ /www.wolframalpha.com/input/?i=x^(1/3),+x+from+-5+to+0
相若方式,标绘X ^ x的。对于x = -1/3,这应该有一个解决方案。然而,这种功能被视为R中未定义对于x <0
因此,不要指望math.h中做魔术,将使其效率低下,你来就可以改变的迹象。
猜你得把负,并把它之后。你可以有一个包装,如果你真的想为你这样做。
function yourPow(double x, double y)
{
if (x < 0)
return -1.0 * pow(-1.0*x, y);
else
return pow(x, y);
}
不通过使用double
投射到(double)
,使用双数字常量代替:
double thingToCubeRoot = -20.*3.2+30;
cout<< thingToCubeRoot/fabs(thingToCubeRoot) * pow( fabs(thingToCubeRoot), 1./3. );
应该做的伎俩!
此外:不包括在C ++项目<math.h>
,但使用代替<cmath>
可替换地,从由buddhabrot陈述的pow
头的理由使用<complex>
pow( x, y )
相同(即,等于)exp( y * log( x ) )
如果日志(x)是无效的,那么POW(X,Y)也是。
同样不能执行0到任何东西的功率,虽然在数学上它应该是0。
C ++ 11具有cbrt
功能(参见例如 HTTP:// en.cppreference.com/w/cpp/numeric/math/cbrt ),所以你可以写类似
#include <iostream>
#include <cmath>
int main(int argc, char* argv[])
{
const double arg = 20.0*(-3.2) + 30.0;
std::cout << cbrt(arg) << "\n";
std::cout << cbrt(-arg) << "\n";
return 0;
}
我没有进入C ++标准,所以我不知道该负的参数是如何处理做...上ideone的 http://ideone.com/bFlXYs 似乎证实,C ++(GCC-4.8.1)延伸的立方根与此规则cbrt(x)=-cbrt(-x)
时x<0
;这个扩展可以看到 http://mathworld.wolfram.com/CubeRoot.html
我一直在寻找肘根,发现这个线程,它下面的代码可能工作时对我说:
#include <cmath>
using namespace std;
function double nth-root(double x, double n){
if (!(n%2) || x<0){
throw FAILEXCEPTION(); // even root from negative is fail
}
bool sign = (x >= 0);
x = exp(log(abs(x))/n);
return sign ? x : -x;
}
我觉得你不应该以数字的n次方根混淆幂。看到好的老维基百科
,因为1/3将总是返回0,因为它会被认为是整数... 尝试用1.0 / 3.0 ... 这就是我的想法,但尝试和实施... 不要忘记申报含有1.0和3.0为双变量...
这里有一个小功能,我敲了起来。
#define uniform() (rand()/(1.0 + RAND_MAX))
double CBRT(double Z)
{
double guess = Z;
double x, dx;
int loopbreaker;
retry:
x = guess * guess * guess;
loopbreaker = 0;
while (fabs(x - Z) > FLT_EPSILON)
{
dx = 3 * guess*guess;
loopbreaker++;
if (fabs(dx) < DBL_EPSILON || loopbreaker > 53)
{
guess += uniform() * 2 - 1.0;
goto retry;
}
guess -= (x - Z) / dx;
x = guess*guess*guess;
}
return guess;
}
它使用牛顿 - 拉夫逊找到立方根。
有时牛顿-Raphson卡住,如果根非常接近于0,那么该衍生物可以 得到大,它可以振荡。所以,我已经夹紧,并迫使它重新启动如果出现这种情况。 如果您需要更精确,你可以改变FLT_EPSILONs。
如果你没有数学库,你可以使用这种方法来计算立方根:
立方根
double curt(double x) {
if (x == 0) {
// would otherwise return something like 4.257959840008151e-109
return 0;
}
double b = 1; // use any value except 0
double last_b_1 = 0;
double last_b_2 = 0;
while (last_b_1 != b && last_b_2 != b) {
last_b_1 = b;
// use (2 * b + x / b / b) / 3 for small numbers, as suggested by willywonka_dailyblah
b = (b + x / b / b) / 2;
last_b_2 = b;
// use (2 * b + x / b / b) / 3 for small numbers, as suggested by willywonka_dailyblah
b = (b + x / b / b) / 2;
}
return b;
}
它是源自于 sqrt
下面的算法。这个想法是 b
和 x / b / b
从立方根开始变大变小 x
. 。因此,两者的平均值更接近于立方根 x
.
平方根和三次根(Python)
def sqrt_2(a):
if a == 0:
return 0
b = 1
last_b = 0
while last_b != b:
last_b = b
b = (b + a / b) / 2
return b
def curt_2(a):
if a == 0:
return 0
b = a
last_b_1 = 0;
last_b_2 = 0;
while (last_b_1 != b and last_b_2 != b):
last_b_1 = b;
b = (b + a / b / b) / 2;
last_b_2 = b;
b = (b + a / b / b) / 2;
return b
与平方根相反, last_b_1
和 last_b_2
因为 b 闪烁,所以需要立方根。您可以修改这些算法来计算四次方根、五次方根等。
感谢我 11 年级的数学老师 Herr Brenner 告诉我这个算法 sqrt
.
表现
我在时钟频率为 16mhz 的 Arduino 上进行了测试: