Python:一般情况下 a 可以四舍五入为 b

发布于 2024-09-26 05:18:43 字数 696 浏览 3 评论 0 原文

作为我正在编写的一些单元测试代码的一部分,我编写了以下函数。其目的是确定“a”是否可以舍入为“b”,无论“a”或“b”有多精确。

def couldRoundTo(a,b):
    """Can you round a to some number of digits, such that it equals b?"""
    roundEnd = len(str(b))
    if a == b:
        return True
    for x in range(0,roundEnd):
        if round(a,x) == b:
            return True
    return False

这是该函数的一些输出:

>>> couldRoundTo(3.934567892987, 3.9)
True
>>> couldRoundTo(3.934567892987, 3.3)
False
>>> couldRoundTo(3.934567892987, 3.93)
True
>>> couldRoundTo(3.934567892987, 3.94)
False

据我所知,它有效。然而,考虑到我对浮点精度问题没有完全掌握,我害怕依赖它。有人可以告诉我这是否是实现此功能的合适方法?如果没有,我该如何改进?

As a part of some unit testing code that I'm writing, I wrote the following function. The purpose of which is to determine if 'a' could be rounded to 'b', regardless of how accurate 'a' or 'b' are.

def couldRoundTo(a,b):
    """Can you round a to some number of digits, such that it equals b?"""
    roundEnd = len(str(b))
    if a == b:
        return True
    for x in range(0,roundEnd):
        if round(a,x) == b:
            return True
    return False

Here's some output from the function:

>>> couldRoundTo(3.934567892987, 3.9)
True
>>> couldRoundTo(3.934567892987, 3.3)
False
>>> couldRoundTo(3.934567892987, 3.93)
True
>>> couldRoundTo(3.934567892987, 3.94)
False

As far as I can tell, it works. However, I'm scared of relying on it considering I don't have a perfect grasp of issues concerning floating point accuracy. Could someone tell me if this is an appropriate way to implement this function? If not, how could I improve it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

画骨成沙 2024-10-03 05:18:43

有人可以告诉我这是否是实现此功能的合适方法吗?

这取决于。如果b不完全等于通常直接从十进制到二进制浮点转换获得的值,则给定函数的行为将会令人惊讶。

例如:

>>> print(0.1, 0.2/2, 0.3/3)
0.1 0.1 0.1
>>> couldRoundTo(0.123, 0.1)
True
>>> couldRoundTo(0.123, 0.2/2)
True
>>> couldRoundTo(0.123, 0.3/3)
False

此操作会失败,因为 0.3 / 3 的计算结果与 0.10.2 / 2 (以及 圆(0.123,1))。

如果没有,我该如何改进?

经验法则:如果您的计算以任何方式特别涉及十进制数字,只需使用 Decimal 即可,以避免所有有损的以 2 为基数的往返。

特别是,Decimal 包含一个名为 quantize 这使得这个问题变得非常简单:

from decimal import Decimal

def roundable(a, b):
    a = Decimal(str(a))
    b = Decimal(str(b))
    return a.quantize(b) == b

Could someone tell me if this is an appropriate way to implement this function?

It depends. The given function will behave surprisingly if b isn't precisely equal to a value that would normally be obtained directly from decimal-to-binary-float conversion.

For example:

>>> print(0.1, 0.2/2, 0.3/3)
0.1 0.1 0.1
>>> couldRoundTo(0.123, 0.1)
True
>>> couldRoundTo(0.123, 0.2/2)
True
>>> couldRoundTo(0.123, 0.3/3)
False

This fails because the calculation of 0.3 / 3 results in a slightly different representation than 0.1 and 0.2 / 2 (and round(0.123, 1)).

If not, how could I improve it?

Rule of thumb: if your calculation specifically involves decimal digits in any way, just use Decimal, to avoid all the lossy base-2 round-tripping.

In particular, Decimal includes a helper called quantize that makes this problem trivially easy:

from decimal import Decimal

def roundable(a, b):
    a = Decimal(str(a))
    b = Decimal(str(b))
    return a.quantize(b) == b
人间☆小暴躁 2024-10-03 05:18:43

一种方法是:

def could_round_to(a, b):
    (x, y) = map(len, str(b).split('.'))
    round_format = "%" + "%d.%df"%(x, y)
    return round_format%a == str(b) 

首先,我们取 x 和 y 中小数点前后的位数。然后,我们构造一个诸如%x.yf的格式。然后,我们向格式字符串提供a

>>> "%2.2f"%123.1234
'123.12'
>>> "%2.2f"%123.1264
'123.13'
>>> "%3.2f"%000.001
'0.00'

现在,剩下的就是比较字符串了。

One way to do it:

def could_round_to(a, b):
    (x, y) = map(len, str(b).split('.'))
    round_format = "%" + "%d.%df"%(x, y)
    return round_format%a == str(b) 

First, we take the number of digits before and after the decimal in x and y. Then, we construct a format such as %x.yf. Then, we supply a to the format string.

>>> "%2.2f"%123.1234
'123.12'
>>> "%2.2f"%123.1264
'123.13'
>>> "%3.2f"%000.001
'0.00'

Now, all that's left is comparing the strings.

另类 2024-10-03 05:18:43

我唯一担心的一点是在解释浮点文字时从字符串到浮点的转换(如 http://docs.python.org/reference/lexical_analysis.html#floating-point-literals)。我不知道是否可以保证浮点文字将计算为最接近给定字符串的浮点数。这个提到的部分是规范中我期望这样的保证的地方。

例如,Java 对于字符串文字的期望更加具体。来自 Double.valueOf(字符串)

[...] [参数] 被视为表示通常的“计算机科学记数法”中的精确十进制值或精确的十六进制值;然后,这个精确的数值在概念上被转换为“无限精确”的二进制值,然后通过 IEEE 754 浮点算术的通常舍入到最接近的规则舍入为 double 类型 [...]

除非你能找到这样的保证Python文档中的任何地方,你可能只是幸运,因为一些早期的浮点库(Python可能依赖的)将字符串仅转换为附近的浮点数,而不是最好的可用浮点数。

不幸的是,在我看来,无论是round,还是float,还是浮点文字规范都没有给你任何可用的保证。

The only point that I'm afraid of is the conversion from strings to floating points when interpreting floating-point literals (as in http://docs.python.org/reference/lexical_analysis.html#floating-point-literals). I don't know if there is any guarantee that a floating-point literal will evaluate to the floating-point number that is closest to the given string. This mentioned section is the place in the specification where I would expect such a guarantee.

For example, Java is much more specific about what to expect from a string literal. From the documentation of Double.valueOf(String):

[...] [the argument] is regarded as representing an exact decimal value in the usual "computerized scientific notation" or as an exact hexadecimal value; this exact numerical value is then conceptually converted to an "infinitely precise" binary value that is then rounded to type double by the usual round-to-nearest rule of IEEE 754 floating-point arithmetic [...]

Unless you can find such a guarantee anywhere in the Python documentation, you can be just lucky, because some earlier floating-point libraries (on which Python might rely) convert a string just to a floating-point number nearby, not to the best available.

Unfortunately, it seems to me that neither round, nor float, nor the specification for floating-point literaly give you any usable guarantee.

梦在夏天 2024-10-03 05:18:43

如果您的目的是测试 round 函数是否会舍入到目标,那么您是对的。否则(还有什么目的?)如果您有疑问,您应该使用 十进制模块

If you purpose is to test if round function will round to the target, then you are correct. Otherwise (what else is the purpose?) if you are in doubt , you should use decimal module

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文