Я поддерживаю скрипт Python, который использует xlrd
для извлечения значений из электронных таблиц Excel, а затем выполняет различные действия с ними. Некоторые ячейки в электронной таблице являются точными числами, и они должны оставаться как таковые. При извлечении значений одной из этих ячеек xlrd
дает мне значение с плавающей запятой
, такое как 0.38288746115497402.
Однако позже мне нужно получить это значение в строку в коде , Выполнение str (значение)
или unicode (значение)
вернет что-то вроде «0.382887461155». Требования говорят, что это не приемлемо; точность должна быть сохранена.
Я попробовал пару вещей, но пока безуспешно. Первый использовал строковое форматирование вещь:
data = "%.40s" % (value)
data2 = "%.40r" % (value)
Но оба производят одно и то же округленное число, «0. Просто подумал, что добавлю, что я использую Python 2.6.4. Я не думаю, что есть какие-то формальные требования, мешающие мне менять версии; это просто не должно испортить любой другой код.
Я автор xlrd. В других ответах и комментариях так много путаницы, чтобы опровергнуть их в комментариях, поэтому я делаю это в ответ.
@katriealex: "" "точность теряется в кишках xlrd" "" --- совершенно необоснованно и неверно. xlrd точно воспроизводит 64-битное число с плавающей запятой, хранящееся в файле XLS.
@katriealex: "" "Возможно, удастся изменить вашу локальную установку xlrd, чтобы изменить приведение типа float" "" --- Я не знаю, зачем вам это нужно; вы не теряете точности, перемещая 16-битное целое число !!! В любом случае этот код используется только при чтении файлов Excel 2.X (которые имеют запись ячейки типа INTEGER). ОП не указывает на то, что он читает такие древние файлы.
@jloubert: Вы, должно быть, ошибаетесь. "%. 40r"% a_float
- это просто причудливый способ получить тот же ответ, что и repr (a_float)
.
@EVERYBODY: Вам не нужно преобразовывать число с плавающей запятой в десятичное, чтобы сохранить точность.Вся суть функции repr ()
заключается в том, что гарантируется следующее:
float(repr(a_float)) == a_float
Python 2.X (X <= 6) repr дает постоянную точность в 17 десятичных цифр, что гарантирует воспроизвести исходное значение. Более поздние версии Pythons (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
Итак, суть в том, что , если вам нужна строка, сохраняющая всю точность объекта с плавающей запятой, используйте saved = repr (the_float_object)
... восстановите значение позже с помощью ] float (сохраняется)
. Это так просто. Нет необходимости в десятичном модуле
.
РЕДАКТИРОВАТЬ: Мой предыдущий ответ очищен, т.к. он не работал должным образом.
Я использую 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'
Обе строки представляют точное содержимое поплавка. Почти все остальное интерпретирует float так, как python думает, что он был, вероятно, предназначен (что в большинстве случаев верно).
РЕДАКТИРОВАТЬ: Я ошибаюсь. Я оставлю этот ответ здесь, чтобы остальная часть цепочки имела смысл, но это неправда. См. Ответ Джона Мачина выше. Спасибо, ребята =).
Если приведенные выше ответы сработают, это здорово - это избавит вас от множества неприятных взломов. Однако, по крайней мере, в моей системе, этого не произойдет. Вы можете проверить это, например,
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
и посмотрим, что произойдет.
Вы можете использовать repr ()
для преобразования в строку без потери точности, а затем преобразовать в десятичное:
>>> from decimal import Decimal
>>> f = 0.38288746115497402
>>> d = Decimal(repr(f))
>>> print d
0.38288746115497402