将Python Float转换为string而不会丢失精度

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

但是,我需要在代码中稍后将此值转换为string。 做任何str(value)unicode(value)将返回类似“0.382887461155”。 要求说这是不可接受的; 精度需要保留。

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

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

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

在search到与在SO和其他地方在互联网上类似问题的人,一个共同的build议是使用Decimal类。 但是我不能改变数据给我的方式(除非有人知道让xlrd返回小数的秘密方法)。 当我尝试这样做时:

 data = Decimal(value) 

我得到一个TypeError: Cannot convert float to Decimal. First convert the float to a string. TypeError: Cannot convert float to Decimal. First convert the float to a string. 但显然我不能将其转换为string,否则我将失去精度。

所以是的,我愿意提供任何build议 – 如果有必要的话,甚至可以是非常粗鲁的/黑客的。 我对Python没有太多的经验(更多的是Java / C#的人),所以如果我在这里有一些基本的误解,请随时纠正我。

编辑:只是想我会补充说我使用Python 2.6.4。 我不认为有任何正式的要求阻止我改变版本; 它只是不要搞乱任何其他的代码。

我是xlrd的作者。 其他答案和评论中有太多的混淆,所以我正在做一个答案。

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

@katriealex:“”“也许可以修改你的本地xlrd安装来改变浮点数”“”—我不知道你为什么要这么做; 你不会失去任何精度浮动一个16位整数! 在任何情况下,只有在读取Excel 2.X文件(具有INTEGERtypes的单元logging)时才使用该代码。 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 

所以底线是如果你想要一个保留所有float对象的精度的string,使用preserved = repr(the_float_object) …稍后通过float(preserved)恢复值。 就这么简单。 不需要decimal模块。

您可以使用repr()将其转换为string而不会丢失精度,然后转换为十进制:

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

编辑:我错了。 我将在这里留下这个答案,所以其余的线程是有道理的,但这不是事实。 请看上面的John Machin的回答。 谢谢你们=)。

如果上面的答案很好 – 这将为您节省很多讨厌的黑客。 但是,至less在我的系统上,他们不会。 你可以用例如检查这个

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

这个数字是你的系统可以从零中区分出来的最小的浮点数。 任何小于这个值的值都可以在执行操作时从任何浮点数中随机添加或减去。 这意味着,至less在我的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 

注意:这只是转换为一个string。 如果需要,您将需要稍后Decimal转换为Decimal

正如已经说过的那样,一个浮动是不准确的 – 所以保持精度可能会有些误导。

这是一种从float对象中获取每一个信息的方法:

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

另一种方式就是这样。

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

两个string都代表了浮点数的确切内容。 当python认为它可能是有意的时(大部分时间是正确的),最重要的是任何其他东西都会解释float。