При преобразовании float
в str
я могу указать количество десятичных знаков, которое я хочу отображать.
'%.6f' % 0.1
> '0.100000'
'%.6f' % .12345678901234567890
> '0.123457'
Но при простом вызове str
на float
в python 2.7 кажется, что по умолчанию используется максимум 12 десятичных знаков.
str(0.1)
>'0.1'
str(.12345678901234567890)
>'0.123456789012'
Где это максимальное количество десятичных знаков определено / задокументировано? Могу ли я получить этот номер программно?
FWIW, цифра «12» (которая характерна для Python 2.x, Python 3.1 и более ранних версий) происходит отсюда: github.com/python/cpython/blob/…. Это изменилось в Python 3.2.
@MarkDickinson Спасибо, теперь, если бы только был способ проверить значение PyFloat_STR_PRECISION из самого Python (при условии, что мы знаем, что используем cpython)
@C_Z_ Бывают ли ситуации, когда вам недостаточно просто проверить версию? Использование 12 цифр было исправлено с самого начала Python (по крайней мере, это есть в версии 0.9.8 Python, выпущенной в 1993 году). Он не менялся до Python 3.2, где он стал неактуальным, потому что str
больше не основывает свой вывод на фиксированном количестве значащих цифр.
Обратите внимание, что в Python 2.0 есть изменение было, когда repr
числа с плавающей запятой изменился на использование 17 значащих цифр, а не 12. Однако точность str
осталась прежней.
Я не верю, что это существует в спецификации языка Python. Однако реализация cpython указывает это. Функция float_repr()
, которая превращает число с плавающей запятой в строку, в конечном итоге вызывает вспомогательную функцию с форматировщиком 'r'
, которая в конечном итоге вызывает служебную функцию, которая жестко кодирует формат до того, что сводится к format(float, '.16g')
. Этот код можно увидеть здесь. Обратите внимание, что это для python3.6.
>>> import math
>>> str(math.pi*4)
12.5663706144
давая максимальное количество знаков значения (как до, так и после десятичной дроби) равным 16. Похоже, что в реализации python2.7 это значение было жестко запрограммировано в .12g
. Что касается того, почему это произошло (а документации несколько не хватает, можно найти здесь.)
Поэтому, если вы пытаетесь узнать, как долго число будет отформатировано при печати, просто получите его длину с помощью .12g
.
def len_when_displayed(n):
return len(format(n, '.12g'))
Как '.16g'
приравнивается к 12 десятичным знакам?
@modesitt: Я думаю, вы неправильно истолковываете код. В версиях Python> = 3.2 и Python 2.7 на большинстве машин float_repr
вообще не использует фиксированное количество значащих цифр. .16g
нигде нет и никогда не было (в Python 2.6 и ранее использовался .17g
). repr
возвращает самую короткую строку десятичных цифр, которая гарантированно округляется до исходного числа с плавающей запятой при округлении до четности. Вы правы насчет того, что .12g жестко запрограммирован для Python str
(а не repr
) для Python 2.x.
Что ж, если вы ищете способ сделать это на чистом питоне, вы всегда можете использовать что-то вроде,
len(str(.12345678901234567890).split('.')[1])
>>>> 12
Я не смог найти его в документации и добавлю его сюда, если найду, но это обходной путь, который, по крайней мере, всегда может вернуть длину точности, если вы хотите знать заранее.
Как вы сказали, всегда кажется, что это 12
, даже при загрузке больших чисел с плавающей запятой.
Из того, что мне удалось найти, это число может сильно варьироваться и в этих случаях найти его эмпирически кажется самым надежным способом делают это. Итак, я бы определил такой простой метод, как этот:
def max_floating_point():
counter = 0
current_length = 0
str_rep = '.1'
while(counter <= current_length):
str_rep += '1'
current_length = len(str(float(str_rep)).split('.')[1])
counter += 1
return current_length
Это вернет вам представление максимальной длины в вашей текущей системе,
print max_floating_point()
>>>> 12
Зачем заморачиваться с циклом со счетчиком, если можно просто протестировать один сверхдлинный поплавок? Тогда функция могла бы быть оберткой без логики для той строки кода, с которой вы начали.
@AlexHurst Моей целью было дать общее решение проблемы OP. Я имею в виду, что вы считаете сверхдлинный поплавок? Не говоря уже о том, что я не уверен, что рабочая нагрузка моего цикла равна беда, как вы подразумевали. OP может запустить этот служебный метод один раз и сохранить возвращенное значение. На самом деле я бы сказал, что это незначительное вычисление.
Количество отображаемых десятичных знаков будет сильно различаться, и не будет способа предсказать, сколько будет отображаться в чистом Python. Некоторые библиотеки, такие как numpy
, позволяют вам устанавливать точность вывода.
Это просто из-за ограничения представления числа с плавающей запятой.
В соответствующих частях ссылки рассказывается о том, как Python выбирает отображение чисел с плавающей запятой.
Python only prints a decimal approximation to the true decimal value of the binary approximation stored by the machine
Python keeps the number of digits manageable by displaying a rounded value instead
Теперь здесь есть вероятность совпадения:
Interestingly, there are many different decimal numbers that share the same nearest approximate binary fraction
Метод выбора десятичных значений для отображения был изменен в Python 3.1 (но последнее предложение подразумевает, что это может быть деталь реализации).
For example, the numbers 0.1 and 0.10000000000000001 are both approximated by 3602879701896397 / 2 ** 55. Since all of these decimal values share the same approximation, any one of them could be displayed while still preserving the invariant eval(repr(x)) == x
Historically, the Python prompt and built-in repr() function would choose the one with 17 significant digits, 0.10000000000000001. Starting with Python 3.1, Python (on most systems) is now able to choose the shortest of these and simply display 0.1.
Хммм, «Python печатает только десятичное приближение к истинному десятичному значению двоичного приближение, хранящегося в машине», кажется неправильным. Значение двоичного числа с плавающей запятой я бы назвал точным, а не двоичным приближение. «... к истинному десятичному значению двоичного файла, хранящегося в машине» звучит яснее. Способ назначения числа с плавающей запятой может иметь некоторое округление, но само сохраненное значение является точным.
@chux Я согласен, в этом отношении документация кажется немного неправильной.
Глядя на результат преобразования случайных чисел, я не смог понять, как определяется длина str()
, например под Python 3.6.6:
>>> str(.123456789123456789123456789)
'0.12345678912345678'
>>> str(.111111111111111111111111111)
'0.1111111111111111'
Вы можете выбрать этот код, который фактически имитирует вашу реальную ситуацию:
import random
maxdec=max(map(lambda x:len(str(x)),filter(lambda x:x>.1,[random.random() for i in range(99)])))-2
Здесь мы проверяем длину ~ 90 случайных чисел в открытом интервале (.1,1) после преобразования (и выводим 0.
слева, отсюда и -2
).
Python 2.7.5 на 64-битном Linux дает мне 12, а Python 3.4.8 и 3.6.6 дают мне 17.
Возможно, лучше продемонстрировать что-то в
< Python 2.7
, поскольку изменения представления float, внесенные в 3.1, были перенесены в 2.7.