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.