Преобразование Python Float в строку без потери точности

StackOverflow https://stackoverflow.com/questions/3481289

Вопрос

Я поддерживаю сценарий Python, который использует xlrd Чтобы получить значения из электронных таблиц Excel, а затем делать с ними разные вещи. Некоторые из клеток в электронной таблице являются высокоточными числами, и они должны оставаться такими. При извлечении значений одного из этих ячеек, xlrd дает мне а float такие как 0,38288746115497402.

Тем не менее, мне нужно получить это значение в строку позже в коде. Делать либо str(value) или unicode(value) вернет что-то вроде "0.382887461155". Требования говорят, что это не приемлемо; Точность должна быть сохранена.

Я пробовал пару вещей до сих пор не успешно. Первый был использование форматирования строки

data = "%.40s" % (value) 
data2 = "%.40r" % (value) 

Но оба производят одно и то же закругленное число, «0.382887461155».

При поиске людей с подобными проблемами на так и в других местах в Интернете общее предложение было использовать Decimal класс. Но я не могу изменить то, как данные переданы мне (если кто-то не знает секретный способ сделать xlrd возвращать десятичные дрожи). И когда я пытаюсь сделать это:

data = Decimal(value)

Я получаю TypeError: Cannot convert float to Decimal. First convert the float to a string. Но, очевидно, я не могу преобразовать его в строку, иначе я потеряю точность.

Итак, да, я открыт для любых предложений - даже действительно валовые / хаки при необходимости. Я не испытываю не страшно с Python (больше парня Java / C #, так что не стесняйтесь, если у меня есть какое-то фундаментальное недопонимание здесь.

Редактировать: Просто подумал, что я добавлю, что я использую Python 2.6.4. Я не думаю, что есть какие-либо официальные требования, останавливая меня от меняющихся версий; Это просто не может испортить любой другой код.

Это было полезно?

Решение

Я автор XLRD. В других ответах есть так много путаницы и комментарии, чтобы опровергнуть в комментариях, поэтому я делаю это в ответ.

@katraeablex: «« Точность утеряна в кишках XLRD »« »- полностью необоснованного и неверного. XLRD воспроизводит именно 64-битный поплавок, который хранится в файле XLS.

@KatraeAlex: «« »может быть возможно изменить свою локальную монтажу XLRD, чтобы изменить литой поплавку« »--- я не знаю, почему вы захотите сделать это; Вы не теряете никакой точности, плавая 16-битное целое число !!! В любом случае, этот код используется только при чтении файлов Excel 2.x (у которых была ячейка INTEGER типа). ОП не дает никаких признаков того, что он читает такие древние файлы.

@jloubert: Вы должны ошибаться. "%.40r" % a_float просто барочный способ получить тот же ответ, что и repr(a_float).

@Everyybody: Вам не нужно преобразовывать поплавок до десятичного времени, чтобы сохранить точность. Вся точка repr() Функция заключается в том, что следующее гарантировано:

float(repr(a_float)) == a_float

Python 2.x (x × <= 6) RIP дает постоянную 17 десятичных цифр точности, так как это гарантированно воспроизвести исходное значение. Позже питоны (2.7, 3.1) дают минимальное количество десятичных цифр, которые будут воспроизводить исходное значение.

Python 2.6.4 (r264:75708, Oct 26 2009, 08:23:19) [MSC v.1500 32 bit (Intel)] on win32
>>> f = 0.38288746115497402
>>> repr(f)
'0.38288746115497402'
>>> float(repr(f)) == f
True

Python 2.7 (r27:82525, Jul  4 2010, 09:01:59) [MSC v.1500 32 bit (Intel)] on win32
>>> f = 0.38288746115497402
>>> repr(f)
'0.382887461154974'
>>> float(repr(f)) == f
True

Итак, нижняя строка в том, что Если вы хотите строку, которая сохраняет всю точность объекта поплавка, используйте preserved = repr(the_float_object) ... верните значение позже float(preserved). Это так просто. Нет необходимости в decimal модуль.

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

Ты можешь использовать repr() Чтобы преобразовать в строку без потери точности, затем преобразовать в десятичное значение:

>>> from decimal import Decimal
>>> f = 0.38288746115497402
>>> d = Decimal(repr(f))
>>> print d
0.38288746115497402

Редактировать: я не прав. Я оставлю этот ответ здесь, поэтому остальная часть нити имеет смысл, но это неправда. Пожалуйста, смотрите ответ Джона Машина выше. Спасибо, ребята =).

Если вышеуказанные ответы на работу, это здорово - это сэкономит вам много неприятного взлома. Однако, по крайней мере, на мою систему, они не будут. Вы можете проверить это с, например,

import sys
print( "%.30f" % sys.float_info.epsilon )

Это число - самый маленький поплавок, который ваша система может отличить от нуля. Что-то меньшее, чем это может быть произвольно добавлено или вычтено из любого поплавка при выполнении операции. Это означает, что, по крайней мере, на моем настройке Python, точность теряется внутри мужества xlrd, и кажется, что вы ничего не можете сделать, не модифицируя его. Что странно; Я бы ожидал, что этот случай произошел раньше, но видимо нет!

Может быть возможно изменить ваш локальный xlrd Установка для изменения float В ролях. Открыть site-packages\xlrd\sheet.py и идти до строки 1099:

...
elif rc == XL_INTEGER:
                    rowx, colx, cell_attr, d = local_unpack('<HH3sH', data)
                    self_put_number_cell(rowx, colx, float(d), self.fixed_BIFF2_xfindex(cell_attr, rowx, colx))
...

Обратите внимание float лить - вы можете попробовать изменить это на decimal.Decimal и посмотрите, что происходит.

РЕДАКТИРОВАТЬ: Очистил мой предыдущий ответ B / C, оно не работает должным образом.

Я на Python 2.6.5, и это работает для меня:

a = 0.38288746115497402
print repr(a)
type(repr(a))    #Says it's a string

Примечание: это просто преобразует в строку. Вам нужно будет преобразовать в Decimal себя позже, если это необходимо.

Как уже говорилось, поплавок вообще не точен - поэтому сохранение точности может быть несколько вводящих в заблуждение.

Вот способ получить каждый последний бит информации из объекта поплавок:

>>> from decimal import Decimal
>>> str(Decimal.from_float(0.1))
'0.1000000000000000055511151231257827021181583404541015625'

Еще один способ будет таким.

>>> 0.1.hex()
'0x1.999999999999ap-4'

Оба струны представляют точное содержимое поплавка. Всего что-то еще интерпретирует поплавок, поскольку Python думает, что это, вероятно, было предназначено (что большую часть времени правильно).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top