Determine la precisión y la escala de un número particular en Python

Tengo una variable en Python que contiene un número de punto flotante (por ejemplo, num = 24654.123 ), y me gustaría determinar la precisión del número y los valores de escala (en el sentido de Oracle), por lo que 123.45678 debería indicarme (8,5), 12.76 Debería darme (4,2), etc.

La primera vez que pensé en usar la representación de la cadena (a través de str o repr ), pero fallan en grandes números (aunque ahora entiendo que las limitaciones de la representación de punto flotante es el problema aquí):

 >>> num = 1234567890.0987654321 >>> str(num) = 1234567890.1 >>> repr(num) = 1234567890.0987654 

Editar:

Buenos puntos abajo. Debería aclarar. El número ya es un flotante y se está enviando a una base de datos a través de cx_Oracle. Estoy tratando de hacer lo mejor que puedo en Python para manejar los flotadores que son demasiado grandes para el tipo de base de datos correspondiente, por debajo de la ejecución de INSERT y el manejo de los errores de Oracle (porque quiero tratar los números en un campo, no en un registro, en un momento). ¿Supongo que map(len, repr(num).split('.')) Es lo más cercano a la precisión y la escala del flotador?

Obtener el número de dígitos a la izquierda del punto decimal es fácil:

 int(log10(x))+1 

El número de dígitos a la derecha del punto decimal es más complicado, debido a la inexactitud inherente de los valores de punto flotante. Necesitaré unos minutos más para resolverlo.

Edición: Basado en ese principio, aquí está el código completo.

 import math def precision_and_scale(x): max_digits = 14 int_part = int(abs(x)) magnitude = 1 if int_part == 0 else int(math.log10(int_part)) + 1 if magnitude >= max_digits: return (magnitude, 0) frac_part = abs(x) - int_part multiplier = 10 ** (max_digits - magnitude) frac_digits = multiplier + int(multiplier * frac_part + 0.5) while frac_digits % 10 == 0: frac_digits /= 10 scale = int(math.log10(frac_digits)) return (magnitude + scale, scale) 

Creo que deberías considerar usar el tipo decimal en lugar de un float . El tipo float dará errores de redondeo porque los números se representan internamente en binario, pero muchos números decimales no tienen una representación binaria exacta.

No es posible con variables de punto flotante. Por ejemplo, escribiendo

 >>> 10.2345 

da:

 10.234500000000001 

Por lo tanto, para obtener 6,4 de esto, tendrá que encontrar una manera de distinguir entre un usuario que ingresa 10.2345 y 10.234500000000001 , lo cual es imposible usando flotadores. Esto tiene que ver con la forma en que se almacenan los números de punto flotante. Usa decimal .

 import decimal a = decimal.Decimal('10.234539048538495') >>> str(a) '10.234539048538495' >>> (len(str(a))-1, len(str(a).split('.')[1])) (17,15) 

(0) Por favor, confirme o rechace: le dan flotantes para usar, esto es inevitable, no puede obtener sus datos como decimales, los tipos de datos de Oracle incluyen tipos basados ​​en decimales, y esta discrepancia fundamental es inevitable. Por favor explique cualquier negación total o parcial.

(1) Su observación de “fallar para grandes números” es engañosa / irrelevante / incorrecta: usted dice que su punto de partida es un flotador, pero 1234567890.0987654321 no puede representarse como un flotante, como lo muestra el resultado de repr ().

(2) Tal vez podría usar la NUEVA repr (Python 2.7 y 3.1) que proporciona la precisión mínima posible de repr (x) que aún satisface float(repr(x)) == x

Por ejemplo, la reproducción antigua (1.1) produce “1.1000000000000001”, la nueva reproducción (1.1) produce “1.1”

Acerca de “Supongo que map (len, repr (num) .split (‘.’)) Es lo más cercano a la precisión y la escala del flotador”: necesita una estrategia para manejar (a) negativa y cero números (b) números como 1.1e20

Excavar en Objetos / floatobject.c debería activar el código C para el nuevo repr () de un objeto flotante, en caso de que necesite usar Python 2.6 o una versión anterior.

(3) Tal vez si nos diera las especificaciones para los tipos de datos de Oracle relevantes, podríamos ayudarlo a diseñar verificaciones para elegir qué tipo puede contener un valor flotante determinado.

Parece que str es mejor elección que repr :

 >>> r=10.2345678 >>> r 10.234567800000001 >>> repr(r) '10.234567800000001' >>> str(r) '10.2345678' 

Básicamente, no se puede con números de punto flotante. Usar el tipo decimal ayudaría y si desea una precisión realmente grande, considere usar gmpy , el puerto de la biblioteca de Precisión Múltiple de GNU para Python.