具有小数精度的分数

7 投票
2 回答
2792 浏览
提问于 2025-04-15 17:52

有没有一个纯Python实现的fractions.Fraction,可以支持long类型作为分子和分母?不幸的是,指数运算似乎是写死的,返回的是浮点数(哎呀!),这至少应该支持使用decimal.Decimal

如果没有这样的实现,我想我可以复制这个库,然后把float()替换成Decimal中的合适函数,但我更希望能找到一个已经被别人测试过的解决方案。

这里有个代码示例:

base = Fraction.from_decimal(Decimal(1).exp())
a = Fraction(69885L, 53L)
x = Fraction(9L, 10L)

print base**(-a*x), type(base**(-a*x))

结果是0.0 <type 'float'>,而答案应该是一个非常小的小数。

更新:我现在有了以下的变通方法(假设对于a**b,两个都是分数;当然,当exp_是浮点数或本身是Decimal时,我需要另一个函数):

def fracpow(base, exp_):
    base = Decimal(base.numerator)/Decimal(base.denominator)
    exp_ = Decimal(exp_.numerator)/Decimal(exp_.denominator)

    return base**exp_

这给出的答案是4.08569925773896097019795484811E-516。

我还是想知道有没有更好的方法来做到这一点,而不需要额外的函数(我猜如果我多使用Fraction类,我会发现其他浮点数也会混进我的结果中)。

2 个回答

0

你可以自己写一个“pow”函数,用来处理分数的幂运算,而不使用浮点数的幂运算。你是想这么做吗?

这个函数可以把分数提升到一个整数的幂,而不需要退回到浮点数运算。

def pow( fract, exp ):
    if exp == 0: 
        return fract
    elif exp % 2 == 0:
        t = pow( fract, exp//2 )
        return t*t
    else:
        return fract*pos( fract, exp-1 )
7

"提升到某个幂"这个操作在有理数中并不是一个封闭的操作(和我们平常的四则运算不一样):没有一个有理数r能满足r == 2 ** 0.5。传说中,毕达哥拉斯(这个定理就是由他提出的)因为他的弟子希帕苏斯证明了这一点而把他杀了;看起来你对毕达哥拉斯的反应很有同感,毕竟你用“应该”这个词的方式有点奇怪;-)

Python中的分数是为了精确计算而设计的,所以不可避免地会出现一些情况,提升一个分数到另一个分数的幂时,结果根本无法返回一个分数;而“应该”这个说法在数学上是不可能的事情上是没法成立的。

所以你能做的最好的事情就是近似你想要的结果,比如得到一个不是精确分数的结果(浮点数通常被认为足够用),然后再用分数进一步近似。大多数现有的纯Python实现(网上有很多rationals.py文件;-))都不太愿意实现**这个操作符,但当然你可以在自己的实现中做出不同的设计决定!-)

撰写回答