我正在维护一个Python脚本,该脚本使用xlrd
从Excel电子表格中检索值,然后使用它们执行各种操作。电子表格中的某些单元格是高精度数字,它们必须保持不变。检索其中一个单元格的值时,xlrd
会给我float
,例如0.38288746115497402.
但是,我需要稍后在代码中将此值转换为字符串。执行str(value)
或unicode(value)
将返回类似“0.382887461155”的内容。要求说这是不可接受的;精度需要保持。
到目前为止,我尝试了几件事,但没有成功。第一个是使用字符串格式化的东西:
data = "%.40s" % (value)
data2 = "%.40r" % (value)
但两者都产生相同的舍入数字,“0.382887461155”。
在SO和其他地方搜索有类似问题的人时,常见的建议是使用Decimal
课程。但是我不能改变数据给我的方式(除非有人知道让xlrd
返回Decimals的秘密方法)。当我尝试这样做时:
data = Decimal(value)
我得到TypeError: Cannot convert float to Decimal. First convert the float to a string.
但显然我无法将其转换为字符串,否则我将失去精确度。
所以,是的,我愿意接受任何建议 - 如果有必要的话,甚至是非常粗暴/粗暴的建议。我对Python(我自己更多的是Java / C#家伙)并不十分熟练,所以如果我在这里遇到某种基本的误解,请随时纠正我。
编辑:我想我会补充说我使用的是Python 2.6.4。我认为没有任何正式要求阻止我改变版本;它只是不得搞乱任何其他代码。答案 0 :(得分:49)
我是xlrd的作者。在评论中反驳的其他答案和评论中存在很多混淆,所以我在答案中这样做。
@katriealex:“”精确度在xlrd的胆量中丢失了“”“ - 完全毫无根据和不真实。 xlrd完全再现了存储在XLS文件中的64位浮点数。
@katriealex:“”“有可能修改你的本地xlrd安装来改变浮动投射”“” - 我不知道你为什么要这样做;浮动一个16位整数,你不会失去任何精度!在任何情况下,只有在读取Excel 2.X文件(具有INTEGER类型的单元格记录)时才使用该代码。 OP没有表明他正在阅读这些古老的文件。
@jloubert:你一定是弄错了。 "%.40r" % a_float
只是一种与repr(a_float)
得到相同答案的巴洛克方式。
@EVERYBODY:您不需要将float转换为小数以保持精度。 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
所以底线是如果你想要一个保留浮动对象的所有精度的字符串,请使用preserved = repr(the_float_object)
...稍后通过float(preserved)
恢复该值。就这么简单。不需要decimal
模块。
答案 1 :(得分:2)
您可以使用repr()
转换为字符串而不会丢失精度,然后转换为十进制:
>>> from decimal import Decimal
>>> f = 0.38288746115497402
>>> d = Decimal(repr(f))
>>> print d
0.38288746115497402
答案 2 :(得分: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
演员阵容 - 您可以尝试将其更改为decimal.Decimal
,看看会发生什么。
答案 3 :(得分:0)
编辑:清除我之前的答案b / c它无法正常工作。
我使用的是Python 2.6.5,这对我有用:
a = 0.38288746115497402
print repr(a)
type(repr(a)) #Says it's a string
注意:这只是转换为字符串。如果需要,您需要稍后自己转换为Decimal
。
答案 4 :(得分:0)
正如已经说过的那样,浮点数根本不精确 - 因此保留精度可能会有些误导。
这是一种从浮动对象中获取最后一点信息的方法:
>>> from decimal import Decimal
>>> str(Decimal.from_float(0.1))
'0.1000000000000000055511151231257827021181583404541015625'
另一种方式就是这样。
>>> 0.1.hex()
'0x1.999999999999ap-4'
两个字符串都代表浮点数的确切内容。所有其他任何东西都解释浮动,因为python认为它可能是有意的(大部分时间都是正确的)。