28

我正在维护一个 Python 脚本,用于xlrd从 Excel 电子表格中检索值,然后用它们做各种事情。电子表格中的某些单元格是高精度数字,它们必须保持原样。在检索这些单元格之一的值时,xlrd给我一个float例如 0.38288746115497402。

但是,我需要稍后在代码中将此值转换为字符串。执行str(value)unicode(value)将返回类似“0.382887461155”的内容。要求说这是不可接受的;需要保持精度。

到目前为止,我已经尝试了几件事,但没有成功。第一个是使用字符串格式化的东西:

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

但两者都产生相同的四舍五入数字“0.382887461155”。

在互联网上搜索 SO 和其他地方有类似问题的人时,一个常见的建议是使用该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。我认为没有任何正式要求阻止我更改版本;它只需要不要弄乱任何其他代码。

4

5 回答 5

54

我是xlrd的作者。其他答案和评论中有很多混乱需要在评论中反驳,所以我在回答中这样做。

@katriealex:“”“精确度在 xlrd 的胆量中丢失了”“”——完全没有根据和不真实。xlrd 精确地再现了存储在 XLS 文件中的 64 位浮点数。

@katriealex:“”“可能可以修改您的本地 xlrd 安装以更改浮动演员”“” ---我不知道您为什么要这样做;浮点 16 位整数不会丢失任何精度!!!在任何情况下,该代码仅在读取 Excel 2.X 文件(具有 INTEGER 类型的单元格记录)时使用。OP没有表明他正在阅读这些古老的文件。

@jloubert:你一定弄错了。"%.40r" % a_float只是获得与 . 相同答案的巴洛克式方式repr(a_float)

@EVERYBODY:您不需要将浮点数转换为小数来保持精度。该repr()功能的重点是保证以下内容:

float(repr(a_float)) == a_float

Python 2.X (X <= 6) repr 给出了恒定的 17 位十进制数字精度,因为这样可以保证重现原始值。后来的 Python(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)... 稍后恢复值 by float(preserved)就是这么简单。不需要decimal模块。

于 2010-08-14T01:09:12.863 回答
3

您可以使用repr()转换为字符串而不会丢失精度,然后转换为小数:

>>> from decimal import Decimal
>>> f = 0.38288746115497402
>>> d = Decimal(repr(f))
>>> print d
0.38288746115497402
于 2010-08-13T23:42:55.583 回答
1

编辑:我错了。我将把这个答案留在这里,以便线程的其余部分有意义,但这不是真的。请参阅上面 John Machin 的回答。谢谢大家=)。

如果上述答案很好 - 它会为您节省很多讨厌的黑客攻击。但是,至少在我的系统上,他们不会。你可以用例如检查这个

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演员表——你可以试着把它改成 adecimal.Decimal看看会发生什么。

于 2010-08-13T23:50:27.733 回答
0

正如已经说过的,浮点数根本不精确 - 因此保持精度可能会有些误导。

这是一种从浮点对象中获取最后一点信息的方法:

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

另一种方式是这样的。

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

两个字符串都代表浮点数的确切内容。几乎所有其他东西都将浮点数解释为python认为它可能是有意的(大多数情况下是正确的)。

于 2010-08-14T15:41:47.367 回答
0

编辑:清除了我之前的答案 b/c 它不能正常工作。

我在 Python 2.6.5 上,这对我有用:

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

注意:这只是转换为字符串。如果需要,您需要Decimal稍后转换为自己。

于 2010-08-13T23:45:20.710 回答