Как мне проверить десятичную точность по умолчанию при преобразовании числа с плавающей запятой в str?

При преобразовании 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'

Где это максимальное количество десятичных знаков определено / задокументировано? Могу ли я получить этот номер программно?

Возможно, лучше продемонстрировать что-то в < Python 2.7, поскольку изменения представления float, внесенные в 3.1, были перенесены в 2.7.

user3483203 07.08.2018 16:20

FWIW, цифра «12» (которая характерна для Python 2.x, Python 3.1 и более ранних версий) происходит отсюда: github.com/python/cpython/blob/…. Это изменилось в Python 3.2.

Mark Dickinson 08.08.2018 09:42

@MarkDickinson Спасибо, теперь, если бы только был способ проверить значение PyFloat_STR_PRECISION из самого Python (при условии, что мы знаем, что используем cpython)

C_Z_ 08.08.2018 23:52

@C_Z_ Бывают ли ситуации, когда вам недостаточно просто проверить версию? Использование 12 цифр было исправлено с самого начала Python (по крайней мере, это есть в версии 0.9.8 Python, выпущенной в 1993 году). Он не менялся до Python 3.2, где он стал неактуальным, потому что str больше не основывает свой вывод на фиксированном количестве значащих цифр.

Mark Dickinson 09.08.2018 09:24

Обратите внимание, что в Python 2.0 есть изменение было, когда repr числа с плавающей запятой изменился на использование 17 значащих цифр, а не 12. Однако точность str осталась прежней.

Mark Dickinson 09.08.2018 09:28
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
21
5
1 789
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я не верю, что это существует в спецификации языка 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 десятичным знакам?

bphi 07.08.2018 16:04

@modesitt: Я думаю, вы неправильно истолковываете код. В версиях Python> = 3.2 и Python 2.7 на большинстве машин float_repr вообще не использует фиксированное количество значащих цифр. .16g нигде нет и никогда не было (в Python 2.6 и ранее использовался .17g). repr возвращает самую короткую строку десятичных цифр, которая гарантированно округляется до исходного числа с плавающей запятой при округлении до четности. Вы правы насчет того, что .12g жестко запрограммирован для Python str (а не repr) для Python 2.x.

Mark Dickinson 08.08.2018 09:27

Что ж, если вы ищете способ сделать это на чистом питоне, вы всегда можете использовать что-то вроде,

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

Зачем заморачиваться с циклом со счетчиком, если можно просто протестировать один сверхдлинный поплавок? Тогда функция могла бы быть оберткой без логики для той строки кода, с которой вы начали.

Alex Hurst 14.08.2018 21:54

@AlexHurst Моей целью было дать общее решение проблемы OP. Я имею в виду, что вы считаете сверхдлинный поплавок? Не говоря уже о том, что я не уверен, что рабочая нагрузка моего цикла равна беда, как вы подразумевали. OP может запустить этот служебный метод один раз и сохранить возвращенное значение. На самом деле я бы сказал, что это незначительное вычисление.

scharette 18.08.2018 02:47
Ответ принят как подходящий

Количество отображаемых десятичных знаков будет сильно различаться, и не будет способа предсказать, сколько будет отображаться в чистом 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 - Reinstate Monica 07.08.2018 17:20

@chux Я согласен, в этом отношении документация кажется немного неправильной.

user3483203 11.08.2018 05:35

Глядя на результат преобразования случайных чисел, я не смог понять, как определяется длина 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.

Другие вопросы по теме