Estaba tratando de escribir un programa para calcular el valor de x^n usando un bucle while :
#include <stdio.h> #include <math.h> int main() { float x = 3, power = 1, copyx; int n = 22, copyn; copyx = x; copyn = n; while (n) { if ((n % 2) == 1) { power = power * x; } n = n / 2; x *= x; } printf("%g^%d = %f\n", copyx, copyn, power); printf("%g^%d = %f\n", copyx, copyn, pow(copyx, copyn)); return 0; } Hasta el valor de 15 para n , la respuesta de mi función creada y la función pow (de math.h ) da el mismo valor; pero, cuando el valor de n excede 15, comienza a dar respuestas diferentes.
No puedo entender por qué hay una diferencia en la respuesta. ¿Es que he escrito mal la función o es otra cosa?
Cuando ejecuto tu código me sale esto:
3^22 = 31381059584.000000 3^22 = 31381059609.000000 Esto se debe a que pow devuelve un double pero su código usa float . Cuando cambié a powf obtuve resultados idénticos:
3^22 = 31381059584.000000 3^22 = 31381059584.000000 Así que simplemente use double todas partes si necesita resultados de alta resolución.
Está mezclando dos tipos diferentes de datos de punto flotante. La función pow usa el tipo double pero su ciclo usa el tipo float (que tiene menos precisión).
Puede hacer que los resultados coincidan usando el tipo double para sus variables x , power y copyx , o llamando a la función powf (que usa el tipo float ) en lugar de pow .
El último ajuste (usando powf ) da el siguiente resultado (compilador clang-cl, Windows 10, 64 bits):
3^22 = 31381059584.000000 3^22 = 31381059584.000000 Y, cambiando la primera línea de tu main a double x = 3, power = 1, copyx; da lo siguiente:
3^22 = 31381059609.000000 3^22 = 31381059609.000000 Tenga en cuenta que, con valores cada vez mayores de n , es cada vez más probable que obtenga una divergencia entre los resultados de su bucle y el valor calculado mediante las funciones de biblioteca pow o powf . En mi plataforma, la versión double da los mismos resultados, hasta el punto en que el valor desborda el rango y se convierte en Infinity . Sin embargo, la versión float comienza a divergir alrededor de n = 55 :
3^55 = 174449198498104595772866560.000000 3^55 = 174449216944848669482418176.000000La matemática de coma flotante es imprecisa (y float es peor que double , ya que tiene incluso menos bits para almacenar los datos; usar double podría retrasar la imprecisión por más tiempo). La función pow (generalmente) usa un algoritmo de exponenciación que minimiza la pérdida de precisión y/o delega a una instrucción a nivel de chip que puede hacer las cosas de manera más eficiente, más precisa o ambas. También podría haber más de una implementación de pow , dependiendo de si le dice al compilador que use matemática de punto flotante estrictamente conforme, lo más rápido posible, la instrucción del hardware, etc.
Su código está bien (aunque usar double obtendría resultados más precisos), pero igualar la precisión mejorada de pow de math.h no es trivial; para cuando lo hayas hecho, lo habrás reinventado. Es por eso que usa la función de biblioteca.
Dicho esto, para las matemáticas de enteros lógicos como las que está utilizando aquí, es probable que la pérdida de precisión de su algoritmo no importe, es puramente el problema de float frente a double en el que pierde precisión del tipo en sí. Como regla general, use de forma predeterminada double y solo cambie a float si está 100 % seguro de que no necesita la precisión y no puede permitirse el costo adicional de memoria/cómputo de double .