寻找小数位的简便方法

99

有没有一种简单的方法或集成函数可以找出浮点数的小数位?

该数字是从字符串中解析出来的,所以一种方法是计算小数点后的数字个数,但这看起来相当笨拙。是否有可能从floatDecimal对象中获取所需的信息?


4
在Python中不确定,但在本地化方面要非常小心,因为某些文化背景在解释数字时","和"."字符有不同的含义。 - jglouie
是的,你说得没错,但在我的情况下,我可以确保以显示的数字格式获得值。不管怎样,还是谢谢你的信息。 :) - Constantinius
2
你可能会陷入旧的精度/准确性陷阱。简而言之,精度是小数点后面有多少位数字,准确性是这些数字中有多少实际上是“正确”的。浮点表示法是计算机只近似表示预期值的领域之一。 - JS.
如果您认为解决方案始于字符串值,为什么不直接从字符串中找到数字的数量,而不是通过 Decimal 类型的方式呢? - Alexander
3
就如我在问题中所陈述的,我认为这种方法过于笨拙,并且无法兼容没有小数位或采用其他格式(例如:科学计数法)的数字。Decimal类似乎是一个很好的抽象化。 - Constantinius
17个回答

126

重复之前其他人说过的话(因为我已经输入了它!),我甚至不确定在浮点数的情况下这样的值是否有意义,因为十进制和二进制表示之间的差异;通常一个可以由有限数量的十进制数字表示的数字,在二进制中只有无限位的表示。

对于 decimal.Decimal 对象,您可以使用 as_tuple 方法检索指数,该方法返回一个命名元组,其中包含 signdigitsexponent 属性:

>>> d = decimal.Decimal('56.4325')
>>> d.as_tuple().exponent
-4

>>> d = decimal.Decimal('56.43256436')
>>> d.as_tuple().exponent
-8

>>> d = decimal.Decimal(str(56.4325))
>>> d.as_tuple().exponent
-4

指数取反就是小数点后的位数,除非指数大于 0


9
这与我所用的Python(版本3.2)不同:decimal.Decimal(56.4325)会返回Decimal('56.43249999999999744204615126363933086395263671875'),而as_tuple().exponent则返回-47。 - Alexander
13
@Alexander,将其尝试转换为字符串。 :) - senderle
2
@Alexander 尝试使用 decimal.Decimal("56.4325")。你之前的尝试先将 56.5425 转换成了浮点数,然后再转换成了十进制数,这就是为什么你得到一个非常接近 56.5425 的带有很多精度的十进制数的原因。 - Brian Fisher
2
@senderle 是的,说得好。我实际上漏掉了单引号。谢谢。 - Alexander
1
@senderle:或者更常见的是decimal.Decimal("5e3").as_tuple().exponent5e3的重要性小于5000 - tricasse
显示剩余13条评论

16

由于浮点数的存储和内部处理方式,"小数位数"不是浮点数具有的属性。

您可以从浮点数中获取任意数量的小数位数。问题在于您需要多少精度。将浮点数转换为字符串时,部分过程是决定准确度。

例如,请尝试:

1.1 - int(1.1)

你会看到答案是:

0.10000000000000009

因此,对于这种情况,小数的位数为17。这可能不是你要找的数字。

但是,你可以使用“round”将数字四舍五入到一定数量的小数位数:

round(3.1415 - int(3.1415), 3)

在这种情况下,小数点后数字被缩减为3位。

你不能从浮点数中获取“小数点后数字的数量”,但你可以决定精度和想要的位数。

将浮点数转换为字符串是做出此类决策的一种方法。


2
这是一篇很棒的文章,因为它指出了我的问题是错误的。但不幸的是,它并没有帮助我解决我的问题。(+1) - Constantinius

8
如果您仅需要找到最高位数字的小数点,即最左边的数字,另一种基本解决方案是:
fraction = 0.001
decimal_places = int(f'{fraction:e}'.split('e')[-1])

这里使用科学计数法 (m x 10^n),其中指数 (n) 表示了系数 (m) 被提高的 10 的次数,也就提供了数字的小数位数,例如上面例子中的 1.000000e-03 对应的指数为 -3。

Decimal(str(fraction)).as_tuple().exponent 的一个重要区别是,科学计数法始终只考虑最显著数字的指数 n,类似于 Decimal().adjusted(),而 Decimal().as_tuple().exponent 返回的是最不显著数字的指数。

字符串格式化为科学计数法只能处理浮点数而不能处理字符串,因此任何浮点算术问题仍然存在。但对于许多用例来说,上述方法可能已经足够。


7
我找到的最快计算小数点后位数并四舍五入数字的方法是:
计算小数点后位数:
a=decimal.Decimal('56.9554362669143476');
lenstr = len(str(a).split(".")[1])

计算、校验和四舍五入:

a=decimal.Decimal('56.9554362669143476');
a = round(float(a),5) if len(str(a).split(".")[1]) > 5 else float(a)

比较:

$ python2 -mtimeit 'import decimal; a=decimal.Decimal('56.9554362669143476'); round(float(a),5) if a.as_tuple().exponent < -5 else float(a)'
10000 loops, best of 3: 32.4 usec per loop
$ python -mtimeit 'import decimal; a=decimal.Decimal('56.9554362669143476'); a = round(float(a),5) if len(str(a).split(".")[1]) > 5 else float(a)'
100000 loops, best of 3: 16.7 usec per loop

6

我需要类似于这样的东西,我测试了其中一些,最快速的是:

str(number)[::-1].find('.')

由于浮点数问题,即使使用Decimal(number),所有的模数都会给我错误的结果(请注意,我需要在脚本中修复整个数据库的价格)。
len(str(Decimal(str(number))) % Decimal(1))) - 2

当我们有浮点数或类似的内容时,需要将字符串转换为十进制相当困难。

这是我的“bench”: https://repl.it/FM8m/17


这个 find() 方法对我来说解决了评估 tkinter Entry() 小部件值的问题。当 'number' 是数字的字符串表示且没有小数时,它返回 -1。有小数时,它可以正确计算小数位数,无论是字符串还是浮点类型。当 'number' 是浮点类型且没有小数时,由于隐含的 .0 小数位,它将返回 1。 - Doc

4
十进制库用于处理类似会计的十进制数。它本身没有返回小数位数的函数。当你意识到它运行的上下文设置为用户所需时,这尤其成为一个问题。
如果你得到一个字符串,你可以将其转换为十进制,但这要么在末尾添加零以达到精度,要么使用舍入设置来截断它。
你最好的选择可能是在你的字符串中拆分点并计算结果子字符串中字符的数量。

2

这将对您有所帮助 ;)

import decimal

def find_decimals(value):
    return (abs(decimal.Decimal(str(value)).as_tuple().exponent))

# Floats
print (find_decimals(123.45678))
print (find_decimals(0.00001))

# Scientific Notation
print (find_decimals(1e-05))

# Strings
print (find_decimals('123.45678'))
print (find_decimals('0.00001'))
print (find_decimals('1e-05'))

输出:

5
5
5
5
5
5

2

最快的方法

precision = lambda x: len(str(x).split('.')[1])

显然要注意数据类型!

我需要这个,因为我需要动态地舍入一系列大大小小的数字。

>>> precision = lambda x: tuple(len(p) for p in str(x).split('.'))
>>> precision(123.345345)
(3, 6)

1
    from math import log10
    my_number = 3.1415927

    int(log10(float(str(my_number)[::-1])))+1

它的工作原理是log10(number)+1将给出小数点左侧的数字数量。

如果我们反转数字,那么左侧的数字以前就在右侧。

    # Reverse the string
    any_string[::-1]

1

如果您知道不会出现解析问题(或者如果您让Python本身或其他库来处理这个问题,希望能够处理本地化问题)...只需解析它并使用modf。返回值是一对值,其中一个是整数部分,另一个是小数部分。


这很有趣,但在我的情况下并不实用,因为我仍然有一个float,无法得到小数位数。 - Constantinius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接