una - C++: ¿cómo puedo probar si un número es potencia de diez?
potencias (9)
Quiero probar si un número double x
es un poder entero de 10. Tal vez podría usar el log10
de cmath y luego probar si x == (int) x
?
Edición: en realidad, mi solución no funciona porque los dobles pueden ser muy grandes, mucho más grandes que int, y también muy pequeños, como fracciones.
¿Qué tal un código como este:
#include <stdio.h>
#define MAX 20
bool check_pow10(double num)
{
char arr[MAX];
sprintf(arr,"%lf",num);
char* ptr = arr;
bool isFirstOne = true;
while (*ptr)
{
switch (*ptr++)
{
case ''1'':
if (isFirstOne)
isFirstOne = false;
else
return false;
break;
case ''0'':
break;
case ''.'':
break;
default:
return false;
}
}
return true;
}
int main()
{
double number;
scanf("%lf",&number);
printf("isPower10: %s/n",check_pow10(number)?"yes":"no");
}
Eso no funcionaría para poderes negativos de 10 sin embargo.
EDITAR: funciona también para poderes negativos.
Dependiendo de la plataforma, su código debe ejecutarse en el registro puede ser muy costoso.
Dado que la cantidad de números que son 10 ^ n (donde n es natural) es muy pequeña, podría ser más rápido usar una tabla de búsqueda codificada.
(Feo seudo código sigue :)
bool isPowerOfTen( int16 x )
{
if( x == 10 // n=1
|| x == 100 // n=2
|| x == 1000 // n=3
|| x == 10000 ) // n=4
return true;
return false;
}
Esto cubre todo el rango int16 y si eso es todo lo que necesita puede ser mucho más rápido. (Dependiendo de la plataforma.)
Me temo que estás en un mundo de dolor. No hay forma de reducir un número de punto flotante muy grande o muy pequeño a una clase BigInt
porque perdió precisión al usar el número de punto flotante pequeño.
Por ejemplo, float
solo tiene 6 dígitos de precisión. Entonces, si representa 10 9 como un float
probable que se convierta de nuevo a 1 000 000 145
o algo así: nada garantiza cuáles serán los últimos dígitos, están fuera de la precisión.
Por supuesto, puede utilizar una representación mucho más precisa, como el double
que tiene 15 dígitos de precisión. Por lo tanto, normalmente debería poder representar los números enteros de 0 a 10 14 con fidelidad.
Finalmente, algunas plataformas pueden tener un tipo long long
con una precisión cada vez mayor.
Pero de todos modos, tan pronto como su valor exceda el número de dígitos disponibles para volver a convertirse en un número entero sin pérdida ... no puede probar que tenga una potencia de diez.
Si realmente necesita esta precisión, mi sugerencia es no usar un número de punto flotante. Hay bibliotecas matemáticas disponibles con BigInt
implementaciones de BigInt
o puede BigInt
su propio rollo (aunque la eficiencia es difícil de lograr).
Qué hay sobre eso:
bool isPow10(double number, double epsilon)
{
if (number > 0)
{
for (int i=1; i <16; i++)
{
if ( (number >= (pow((double)10,i) - epsilon)) &&
(number <= (pow((double)10,i) + epsilon)))
{
return true;
}
}
}
return false;
}
Supongo que si el rendimiento es un problema, los pocos valores podrían precomputarse, con o sin el épsilon según las necesidades.
Si no necesitas que sea rápido, usa la recursión. Pseudocódigo
bool checkifpoweroften(double Candidadte)
if Candidate>=10
return (checkifpoweroften(Candidadte/10)
elsif Candidate<=0.1
return (checkifpoweroften(Candidadte*10)
elsif Candidate == 1
return 1
else
return 0
Aún debe elegir entre falsos positivos y falsos negativos y agregar tolerancias según corresponda, como lo indican otras respuestas. Las tolerancias deben aplicarse a todas las comparaciones o, por ejemplo, 9.99999999 fallaría en la comparación> = 10.
Su solución suena bien, pero yo reemplazaría la comparación exacta por una de tolerancia.
double exponent = log10(value);
double rounded = floor(exponent + 0.5);
if (fabs(exponent - rounded) < some_tolerance) {
//Power of ten
}
Una tabla de búsqueda será, con mucho, la forma más rápida y precisa de hacerlo; Solo cerca de 600 potencias de 10 se pueden representar como dobles. Puede usar una tabla hash, o si la tabla está ordenada de menor a mayor, puede buscarla rápidamente con un corte binario.
Esto tiene la ventaja de que obtendrá un "acierto" si y solo si su número es exactamente el doble IEEE posible más cercano al poder de 10. Si esto no es lo que desea, debe ser más preciso sobre cómo exactamente quisiera que su solución maneje el hecho de que muchas potencias de 10 no se pueden representar exactamente como dobles.
La mejor manera de construir la tabla es probablemente usar la conversión de cadena -> flotante; de esta manera, esperamos que los autores de su biblioteca ya hayan resuelto el problema de cómo hacer la conversión de una manera que ofrezca la respuesta más precisa posible.
Una variante de this :
double log10_value= log10(value);
double integer_value;
double fractional_value= modf(log10_value, &integer_value);
return fractional_value==0.0;
Tenga en cuenta que la comparación con 0.0
es exacta en lugar de dentro de un épsilon particular, ya que quiere asegurarse de que log10_value
sea un número entero.
EDITAR: Ya que esto provocó un poco de controversia debido a que log10
posiblemente sea impreciso y el entendimiento genérico de que no debería comparar los dobles sin un épsilon, aquí hay una manera más precisa de determinar si un doble es una potencia de 10 usando solo propiedades de poderes de 10 y IEEE 754 dobles.
Primero, una aclaración: un doble puede representar hasta 1E22, ya que 1e22 tiene solo 52 bits significativos. Afortunadamente, 5 ^ 22 también tiene 52 bits significativos, por lo que podemos determinar si un doble es (2*5)^n
para n= [0, 22]
:
bool is_pow10(double value)
{
int exponent;
double mantissa= frexp(value, &exponent);
int exponent_adjustment= exponent/10;
int possible_10_exponent= (exponent - exponent_adjustment)/3;
if (possible_10_exponent>=0 &&
possible_10_exponent<=22)
{
mantissa*= pow(2.0, exponent - possible_10_exponent);
return mantissa==pow(5.0, possible_10_exponent);
}
else
{
return false;
}
}
Dado que 2^10==1024
, eso agrega un poco de significado adicional que debemos eliminar de la posible potencia de 5.
bool power_of_ten(double x) {
if(x < 1.0 || x > 10E15) {
warning("IEEE754 doubles can only precisely represent powers "
"of ten between 1 and 10E15, answer will be approximate.");
}
double exponent;
// power of ten if log10 of absolute value has no fractional part
return !modf(log10(fabs(x)), &exponent);
}