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

4 minutos de lectura

avatar de usuario
martín pescador phuoc

Recientemente escribo 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!

  • que tipo es sectionsy ¿cómo se inicializa?

    – pmdj

    14 de marzo de 2012 a las 14:49

avatar de usuario
tarea

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

EDITAR: En caso de cout << pow(10.0, 5); parece que la salida está redondeada, pero no tengo ningún documento de respaldo en este momento 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 eso se va redondeando.

  • Esto no explica por qué la impresión directa no imprime 9999.9999999

    – Lucian Grigore

    14/03/2012 a las 14:50

  • @Luchian Grigore, ¿es posible que en ese caso se esté redondeando? Me gustaría escuchar esa explicación también.

    – taskinoor

    14/03/2012 a las 14:52

  • Si la precisión predeterminada es menor, el número de lugares decimales se redondearía.

    – Bobtfish

    14 de marzo de 2012 a las 15:01

  • @Kingfisher, en mi máquina int i = pow(10, 5) da como resultado 10000, pero pow(10.0, 5) no. Parece sections es doble en su código. si realmente lo necesitas sections ser doble entonces en lugar de asignarlo a i necesitas redondear la potencia.

    – taskinoor

    14/03/2012 a las 15:10

  • std::cout redondeará los flotantes a un número específico de cifras significativas, que se puede cambiar usando el std::setprecision manipulador. Entonces, si intentas generar 99.9999 (que recuerde es en realidad la aproximación más cercana a la que puede manejar, no el número exacto) con una precisión de, digamos, 4, el redondeo “llevará el 1” hasta el final e imprimirá 100.

    – Bobtfish

    14 de marzo de 2012 a las 15:13

  • No se puede usar ceil(pow()) porque también puede estar apagado

    – Antti Haapala — Слава Україні

    13 de agosto de 2020 a las 7:22

Cuando se usa con exponentes fraccionarios, pow(x,y) se evalúa comúnmente como exp(log(x)*y); tal fórmula se corregiría matemáticamente si se evaluara con una precisión infinita, pero en la práctica puede dar como resultado errores de redondeo. Como han señalado otros, un valor de 9999,999999999 cuando se convierte en un número entero dará como resultado 9999. Algunos lenguajes y bibliotecas usan esta formulación todo el tiempo cuando usan 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 el pow función, 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 extraño, el resultado debería ser -pow(-x,y). Parecería lógico que cuando y no tiene parte fraccionaria una biblioteca que va a pasar por la molestia de tratar con un negativo x El valor debe usar la multiplicación iterada, pero no conozco ninguna especificación que indique que debe hacerlo.

En cualquier caso, si está tratando de elevar un número entero a una potencia, es casi seguro que es mejor usar matemáticas de números enteros para el cálculo o, si el número entero a elevar es una constante o siempre será pequeño, simplemente use una tabla de búsqueda. (elevar los números de 0 a 15 por cualquier potencia que cabría en un número entero de 64 bits requeriría solo una tabla de 4096 elementos).

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

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

el decimal .99999 se corta y obtienes 9999.

mientras que imprimir directamente o compararlo con 10000 no es un problema porque en cierto sentido está agotado.

avatar de usuario
Marca B

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ás recogiendo std::pow o C pow 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 double. No debe ocurrir redondeo, truncamiento ni nada por el estilo.

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

Ahora lo que sospecho que está pasando es que sections es no se le asigna el literal 10 directamente, pero en su lugar se lee o calcula internamente de modo que su valor es algo así como 9.9999999999999que cuando se eleva a la cuarta potencia genera un número como 9999.9999999. Esto luego se trunca al número entero 9999 que se muestra.

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

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad