Precisión de repr (f), str (f), print (f) cuando f es float

Si corro:

>>> import math >>> print(math.pi) 3.141592653589793 

Entonces pi se imprime con 16 dígitos,

Sin embargo, según:

 >>> import sys >>> sys.float_info.dig 15 

Mi precisión es de 15 dígitos.

Por lo tanto, debo confiar en el último dígito de ese valor (es decir, que el valor de π es 3.141592653589793nnnnnn).

TL; DR

El último dígito de str(float) o repr(float) puede ser “incorrecto”, ya que parece que la representación decimal no está correctamente redondeada.

 >>> 0.100000000000000040123456 0.10000000000000003 

Pero ese valor sigue siendo más cercano al original que 0.1000000000000000 (con 1 dígito menos).

En el caso de math.pi , la aproximación decimal de pi es 3.14159265358979 3238463 …, en este caso el último dígito es correcto.

El sys.float_info.dig indica cuántos dígitos decimales se garantiza que sean siempre precisos.


La salida predeterminada para str(float) y repr(float) en Python 3.1+ (y 2.7 para repr ) es la cadena más corta que cuando se convierte a float devolverá el valor original; En caso de ambigüedad, el último dígito se redondea al valor más cercano. Un flotador proporciona ~ 15.9 dígitos decimales de precisión; pero en realidad se requiere una precisión de hasta 17 dígitos decimales para representar 53 dígitos binarios sin ambigüedad,

Por ejemplo, 0.10000000000000004 encuentra entre 0x1.999999999999dp-4 y 0x1.999999999999cp-4 , pero este último está más cerca; estos 2 tienen las expansiones decimales

 0.10000000000000004718447854656915296800434589385986328125 

y

 0.100000000000000033306690738754696212708950042724609375 

respectivamente. Claramente este último está más cerca, por lo que se elige la representación binaria.

Ahora, cuando estos se vuelven a convertir en cadena con str() o repr() , se elige la cadena más corta que produce exactamente el mismo valor; para estos 2 valores son 0.10000000000000005 y 0.10000000000000003 respectivamente


La precisión de un double en IEEE-754 es de 53 dígitos binarios; en decimal puede calcular la precisión tomando un logaritmo basado en 10 de 2 ^ 53,

 >>> math.log(2 ** 53, 10) 15.954589770191001 

Es decir, casi 16 dígitos de precisión. La precisión de float_info indica cuánto puede esperar estar siempre presentable, y este número es 15, ya que hay algunos números con 16 dígitos decimales que no se pueden distinguir.


Sin embargo, esta no es toda la historia. Internamente, lo que sucede en Python 3.2+ es que float.__str__ y float.__repr__ terminan llamando al mismo método C float_repr :

 float_repr(PyFloatObject *v) { PyObject *result; char *buf; buf = PyOS_double_to_string(PyFloat_AS_DOUBLE(v), 'r', 0, Py_DTSF_ADD_DOT_0, NULL); if (!buf) return PyErr_NoMemory(); result = _PyUnicode_FromASCII(buf, strlen(buf)); PyMem_Free(buf); return result; } 

El PyOS_double_to_string entonces, para el modo 'r' (en espera de repr), llama al _Py_dg_dtoa con el modo 0, que es una rutina interna para convertir el doble en una cadena, o snprintf con %17g para aquellas plataformas para las cuales el _Py_dg_dtoa no funciona

El comportamiento de snprintf es completamente dependiente de la plataforma, pero si se usa _Py_dg_dtoa (por lo que entiendo, debería usarse en la mayoría de las máquinas), debería ser predecible.

El modo _Py_dg_dtoa 0 se especifica de la siguiente manera:

0 ==> cadena más corta que produce d cuando se lee y se redondea al más cercano.

Entonces, eso es lo que sucede: la cadena producida debe reproducir exactamente el valor double cuando se lee, y debe ser la representación más corta posible, y entre las múltiples representaciones decimales que se leerían, sería la más cercana a la valor binario. Ahora, esto también podría significar que el último dígito de la expansión decimal no coincide con el valor original redondeado en esa longitud, solo que la representación decimal es lo más cercana posible a la representación binaria original. Así YMMV.