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

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

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

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

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

Но оба производят одно и то же округленное число, «0. Просто подумал, что добавлю, что я использую Python 2.6.4. Я не думаю, что есть какие-то формальные требования, мешающие мне менять версии; это просто не должно испортить любой другой код.

26
задан jloubert 16 August 2010 в 22:20
поделиться

5 ответов

Я автор 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 (сохраняется) . Это так просто. Нет необходимости в десятичном модуле .

50
ответ дан 28 November 2019 в 07:00
поделиться

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

Я использую Python 2.6.5, и у меня это работает:

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

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

0
ответ дан 28 November 2019 в 07:00
поделиться

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

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

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

Другой способ был бы таким.

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

Обе строки представляют точное содержимое поплавка. Почти все остальное интерпретирует float так, как python думает, что он был, вероятно, предназначен (что в большинстве случаев верно).

0
ответ дан 28 November 2019 в 07:00
поделиться

РЕДАКТИРОВАТЬ: Я ошибаюсь. Я оставлю этот ответ здесь, чтобы остальная часть цепочки имела смысл, но это неправда. См. Ответ Джона Мачина выше. Спасибо, ребята =).

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

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 и посмотрим, что произойдет.

1
ответ дан 28 November 2019 в 07:00
поделиться

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

>>> from decimal import Decimal
>>> f = 0.38288746115497402
>>> d = Decimal(repr(f))
>>> print d
0.38288746115497402
2
ответ дан 28 November 2019 в 07:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: