encontrando raiz cúbica em C++?
Pergunta
Coisas estranhas acontecem quando tento encontrar a raiz cúbica de um número.
O código a seguir me retorna indefinido.Em cmd:-1.#IND
cout<<pow(( double )(20.0*(-3.2) + 30.0),( double )1/3)
Embora este funcione perfeitamente bem.Em cmd:4.93242414866094
cout<<pow(( double )(20.0*4.5 + 30.0),( double )1/3)
Do ponto de vista matemático, deve funcionar, pois podemos obter a raiz cúbica de um número negativo.Pow é da biblioteca math.h do Visual C++ 2010.Alguma ideia?
Solução
pow(x, y)
a partir de <cmath>
não funciona se X for negativo e Y não integral.
Esta é uma limitação de std::pow
, conforme documentado no padrão C e em CPPPREFERÊNCIA:
Manipulação de erros
- Os erros são relatados conforme especificado em Math_errhandling
- Se a base for finita e negativa e o exp for é finito e não inteiro, ocorre um erro de domínio e um erro de intervalo pode ocorrer.
- Se a base for zero e o exp for zero, poderá ocorrer um erro de domínio.
- Se a base for zero e o EXP for negativo, pode ocorrer um erro de domínio ou um erro de pólo.
Existem algumas maneiras de contornar essa limitação:
Rooting de cubo é o mesmo que levar algo para o poder de 1/3, para que você possa fazer
std::pow(x, 1/3.)
.Em C ++ 11, você pode usar
std::cbrt
. C ++ 11 introduziu funções de raiz quadrada e raízes cubos, mas nenhuma função raiz genérica que supera as limitações destd::pow
.
Outras dicas
O poder 1/3
é um caso especial. Em geral, os poderes não integrais de números negativos são complexos. Não seria prático para o Pow verificar se há casos especiais como raízes inteiras e, além disso, 1/3
Como duplo não é exatamente 1/3!
Não sei sobre o Visual C ++ Pow, mas minha página do meu homem diz em erros:
EDOM
O argumentox
é negativo ey
não é um valor integral. Isso resultaria em um número complexo.
Você precisará usar uma função raiz de cubo mais especializada se desejar raízes de cubo de números negativos - ou cortar cantos e assumir o valor absoluto, depois pegue a raiz do cubo e multiplique a sinal novamente.
Observe que, dependendo do contexto, um número negativo x
para o 1/3
O poder não é necessariamente a raiz negativa do cubo que você está esperando. Poderia ser facilmente a primeira raiz complexa, x^(1/3) * e^(pi*i/3)
. Esta é a convenção que Mathematica usa; Também é razoável apenas dizer que é indefinido.
Enquanto (-1)^3 = -1, você não pode simplesmente tomar um poder racional de um número negativo e esperar uma resposta real. Isso ocorre porque existem outras soluções para esse expoente racional de natureza imaginária.
http://www.wolframalpha.com/input/?i=x^(1/3),+x+From+-5+To+0
Da mesma forma, plot x^x. Para x = -1/3, isso deve ter uma solução. No entanto, essa função é considerada indefinida em r para x <0.
Portanto, não espere que o Math.h faça mágica que o tornaria ineficiente, basta mudar os sinais.
Acho que você precisa tirar o negativo e colocá -lo depois. Você pode fazer com que um invólucro faça isso por você, se realmente quiser.
function yourPow(double x, double y)
{
if (x < 0)
return -1.0 * pow(-1.0*x, y);
else
return pow(x, y);
}
Não escalada para double
usando (double)
, use uma constante numérica dupla: em vez disso:
double thingToCubeRoot = -20.*3.2+30;
cout<< thingToCubeRoot/fabs(thingToCubeRoot) * pow( fabs(thingToCubeRoot), 1./3. );
Deve fazer o truque!
Além disso: não inclua <math.h>
em projetos C ++, mas use <cmath>
em vez de.
Alternativamente, use pow
de <complex>
Cabeçalho pelas razões declaradas por Buddhabrot
pow( x, y )
é o mesmo que (ou seja, equivalente a) exp( y * log( x ) )
Se o log (x) for inválido, o pow (x, y) também é.
Da mesma forma, você não pode executar 0 ao poder de nada, embora matematicamente deva ser 0.
C ++ 11 tem o cbrt
função (veja por exemplo http://en.cppreference.com/w/cpp/numeric/math/cbrt) para que você possa escrever algo como
#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;
}
Eu não tenho acesso ao padrão C ++, então não sei como o argumento negativo é tratado ... um teste em ideone http://ideone.com/bflxys parece confirmar que C ++ (GCC-4.8.1) estende a raiz do cubo com esta regra cbrt(x)=-cbrt(-x)
quando x<0
; Para esta extensão, você pode ver http://mathworld.wolfram.com/cubeoot.html
Eu estava procurando por raízes de cúbitos e encontrei esse tópico e me ocorre que o código a seguir poderia funcionar:
#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;
}
Eu acho que você não deve confundir a exponenciação com a raiz NTH de um número. Veja o bom velho Wikipedia
porque 1/3 sempre retornará 0 pois será considerado um número inteiro ...tente com 1.0/3.0...é o que penso, mas tento implementar...e não esqueça de declarar variáveis contendo 1.0 e 3.0 como double...
Aqui está uma pequena função que eu bata.
#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;
}
Ele usa Newton-Raphson para encontrar uma raiz de cubo.
Em algum momento, Newton -Raphson fica preso, se a raiz estiver muito próxima de 0, o derivado pode ficar grande e pode oscilar. Então, eu prendi e forcei a reiniciar se isso acontecer. Se você precisar de mais precisão, poderá alterar os FLT_EPSILONS.
Se você não possui biblioteca de matemática, pode usar desta maneira para calcular a raiz cúbica:
raiz cúbica
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;
}
Deriva do sqrt
Algoritmo abaixo. A ideia é que b
e x / b / b
maior e menor da raiz cúbica de x
. Então, a média de ambos está mais próxima da raiz cúbica de x
.
Raiz quadrada e raiz cúbica (em 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
Em contraste com a raiz quadrada, last_b_1
e last_b_2
são necessários na raiz cúbica porque B oscilantes. Você pode modificar esses algoritmos para calcular a quarta raiz, a quinta raiz e assim por diante.
Obrigado ao meu professor de matemática, Herr Brenner, na 11ª série, que me contou este algoritmo para sqrt
.
atuação
Eu o testei em um Arduino com frequência de relógio de 16MHz: