Por qué pow (10,5) = 9.999 en C ++

Recientemente escribí un bloque de código:

const int sections = 10; for(int t= 0; t < 5; t++){ int i = pow(sections, 5- t -1); cout << i << endl; } 

Y el resultado es incorrecto:

 9999 1000 99 10 1 

Si uso solo este código:

 for(int t = 0; t < 5; t++){ cout << pow(sections,5-t-1) << endl; } 

El problema ya no ocurre:

 10000 1000 100 10 1 

¿Alguien me da una explicación? ¡muchas gracias!

Debido a la representación de los valores de coma flotante, pow(10.0, 5) podría ser 9999.9999999 o algo así. Cuando lo asigna a un número entero que se truncó.

EDITAR: En caso de cout << pow(10.0, 5); parece que la salida está redondeada, pero ahora no tengo ningún documento de respaldo que lo confirme.

EDIT 2: El comentario hecho por BoBTFish y esta pregunta confirma que cuando pow(10.0, 5) se usa directamente en cout se redondea.

Cuando se usa con exponentes fraccionarios, pow (x, y) comúnmente se evalúa como exp(log(x)*y) ; dicha fórmula corregirá matemáticamente si se evalúa con precisión infinita, pero en la práctica puede dar lugar a errores de redondeo. Como han señalado otros, un valor de 9999.999999999 cuando se convierte en un número entero dará 9999. Algunos lenguajes y bibliotecas usan dicha formulación todo el tiempo cuando se usa un operador de exponenciación con un exponente de coma flotante; otros intentan identificar cuándo el exponente es un número entero y usan la multiplicación iterada cuando corresponde. Buscando documentación para la función pow , parece que se supone que funciona cuando x es negativo y y no tiene parte fraccionaria (cuando x es negativo y `y es par, el resultado debe ser pow(-x,y) ; cuando y es impar, el resultado debería ser -pow(-x,y) . Parecería lógico que cuando y no tiene parte fraccionaria, una biblioteca que tenga problemas para lidiar con un valor x negativo use multiplicación iterada, pero No sé de ninguna especificación que dicte que deba.

En cualquier caso, si intenta elevar un número entero a una potencia, es casi seguro que es mejor utilizar números enteros para el cálculo o, si el entero que se va a generar es una constante o siempre será pequeño, simplemente use una tabla de búsqueda (elevar los números del 0 al 15 por cualquier potencia que se ajuste a un entero de 64 bits requeriría solo una tabla de 4.096 elementos).

Desde aquí

Mirando la función pow() : double pow (double base, double exponent); sabemos que los parámetros y el valor de retorno son todos de tipo double . Pero las variables num , i y res son todas de tipo int en el código anterior, cuando se transfiere int a double o double a int , puede causar pérdida de precisión. Por ejemplo (quizás no riguroso), la unidad de punto flotante (FPU) calcula pow(10, 4)=9999.99999999 , luego int(9999.9999999)=9999 por tipo de transformación en C ++.

¿Cómo resolverlo?

Solución1

Cambiar el código:

  const int num = 10;

     for (int i = 0; i <5; ++ i) {
        doble res = pow (num, i);
        cout << res << endl;
     }

Solution2

Reemplace la unidad de punto flotante (FPU) que tiene una mayor precisión de cálculo en el tipo double . Por ejemplo, usamos SSE en la CPU de Windows. En Code :: Block 13.12, podemos hacer estos pasos para alcanzar el objective: Configuración -> Configuración del comstackdor -> GNU GCC Compile -> Otras opciones, agregue

-mfpmath=sse -msse3

La imagen es la siguiente:

agregue -mfpmath = sse -msse3 http://sofes.miximages.com/c%2B%2B/change SSE in CB.png

Lo que sucede es que la función pow devuelve un doble así que cuando haces esto

 int i = pow(sections, 5- t -1); 

los cortes .99999 decimales de y obtienes 9999.

mientras imprime directamente o lo compara con 10000 no es un problema porque está desviado en cierto sentido.

Si el código en su primer ejemplo es el código exacto que está ejecutando, entonces tiene una biblioteca con errores. Independientemente de si está recogiendo std::pow o std::pow C que toma dobles, incluso si se elige la versión doble, 10 es exactamente representable como un double . Como tal, la exponenciación es exactamente representable como un double . No debe producirse redondeo, truncamiento ni nada de eso.

Con g ++ 4.5 no pude reproducir tu comportamiento (extraño) incluso usando -ffast-math y -O3 .

Ahora lo que sospecho que está sucediendo es que a las sections no se les está asignando el literal 10 directamente, sino que se leen o calculan internamente, de modo que su valor es algo así como 9.9999999999999 , que cuando se eleva a la cuarta potencia genera un número como 9999.9999999 . Esto se trunca al número entero 9999 que se muestra.

Dependiendo de sus necesidades, es posible que desee redondear el número fuente o el número final antes de la asignación en un int. Por ejemplo: int i = pow(sections, 5- t -1) + 0.5; // Add 0.5 and truncate to round to nearest. int i = pow(sections, 5- t -1) + 0.5; // Add 0.5 and truncate to round to nearest.

Debe haber alguna función de pow rota en el espacio de nombres global. Entonces std::pow se usa “automáticamente” en su segundo ejemplo debido a ADL.

O eso o t es en realidad una cantidad de punto flotante en su primer ejemplo, y se encuentra con errores de redondeo.

¿Cuál es el tipo de t en tu primer bloque de código? Si no es una int, podría ser un problema de redondeo.

Estás asignando el resultado a un int. Eso lo coacciona, truncando el número.

Esto debería funcionar bien:

 for(int t= 0; t < 5; t++){ double i = pow(sections, 5- t -1); cout << i << endl; } 

Lo que ocurre es que tu respuesta es en realidad 99.9999 y no exactamente 100. Esto es porque pow es doble. Entonces, puedes arreglar esto usando i = ceil(pow()) .

Tu código debería ser:

 const int sections = 10; for(int t= 0; t < 5; t++){ int i = ceil(pow(sections, 5- t -1)); cout << i << endl; }