Java 给定两个双精度,如何确定它们的商是否精确?

Java 给定两个双精度,如何确定它们的商是否精确?,java,math,floating-point,ieee-754,Java,Math,Floating Point,Ieee 754,给定两个double值,p和q,我如何确定它们的商: double result = p / q; 根据p和q的二进制值是否是精确的结果 也就是说,result是否正好等于p和q的数学除法 显然,这对于某些值是正确的,例如1.0/2.0,而对于其他值,例如1.0/5.0,则是错误的,因此我正在寻找一种惯用且准确的方法来区分这些情况 看起来浮点模数p%q==0可能会起作用,但我不确定 您可以使用BigDecimal查看除法是否精确: private static boolean canDivid

给定两个
double
值,
p
q
,我如何确定它们的商:

double result = p / q;
根据
p
q
的二进制值是否是精确的结果

也就是说,
result
是否正好等于
p
q
的数学除法

显然,这对于某些值是正确的,例如
1.0/2.0
,而对于其他值,例如
1.0/5.0
,则是错误的,因此我正在寻找一种惯用且准确的方法来区分这些情况


看起来浮点模数
p%q==0
可能会起作用,但我不确定

您可以使用
BigDecimal
查看除法是否精确:

private static boolean canDivideExact(double p, double q) {
  double r = p / q;
  BigDecimal d = new BigDecimal(r);
  return d.multiply(new BigDecimal(q)).compareTo(new BigDecimal(p)) == 0;
}
例如:

System.out.println(canDivideExact(1, 2)); //true
System.out.println(canDivideExact(1, 3)); //false

您可以使用
BigDecimal
查看除法是否精确:

private static boolean canDivideExact(double p, double q) {
  double r = p / q;
  BigDecimal d = new BigDecimal(r);
  return d.multiply(new BigDecimal(q)).compareTo(new BigDecimal(p)) == 0;
}
例如:

System.out.println(canDivideExact(1, 2)); //true
System.out.println(canDivideExact(1, 3)); //false

我认为您的
p%q==0
解决方案不起作用:它检查
p
是否可以被
q
平均分割,也就是说,
p/q
是否是一个整数。例如,
1.0%2.0==1.0
,尽管它可以精确地表示为双精度:
1.0/2.0==0.5

有趣的是,IEEE 754也作为Java浮点实现的基础,它正好满足了您的需要。如果浮点运算产生不精确的结果,则会引发异常(IEEE意义上的异常),默认情况下会更新状态字,因此您可以通过检查此状态字始终检查结果是否精确。不幸的是,Java选择不让该状态可访问


如果你想继续使用Java,你要么使用assylia的基于
BigDecimal
的解决方案,要么尝试通过JNI访问这些错误标志:在C(C99以后)中,你可以用
fetestexcept(FE_incexecpt)
测试结果是否准确。不过,我不知道这是否有效。

我认为您的
p%q==0
解决方案不起作用:它检查
p
是否可以被
q
平均分割,也就是说,
p/q
是否是一个整数。例如,
1.0%2.0==1.0
,尽管它可以精确地表示为双精度:
1.0/2.0==0.5

有趣的是,IEEE 754也作为Java浮点实现的基础,它正好满足了您的需要。如果浮点运算产生不精确的结果,则会引发异常(IEEE意义上的异常),默认情况下会更新状态字,因此您可以通过检查此状态字始终检查结果是否精确。不幸的是,Java选择不让该状态可访问


如果你想继续使用Java,你要么使用assylia的基于
BigDecimal
的解决方案,要么尝试通过JNI访问这些错误标志:在C(C99以后)中,你可以用
fetestexcept(FE_incexecpt)
测试结果是否准确。不过,我不知道这是否有效。

这里有两种方法不涉及使用
BigDecimal
。如前所述,这两种方法都不适用于低于正常值的情况,但如果您能够避免低于正常值、下溢和上溢,那么这两种方法都会得到很好的结果。我希望这两种方法都可以适用于低于正常的情况,但我还没有考虑如何做到这一点

  • 对于整数
    e
    和带有
    m
    奇数的
    m
    ,任何有限非零
    double
    x
    都可以以
    x=m2^e
    的形式唯一写入。让我们把
    m
    称为
    x
    奇数部分。现在给定两个非零的有限双精度
    x
    y
    ,并假设避免了溢出和下溢,
    x/y
    是可精确表示的,当且仅当
    x
    的奇数部分是
    y
    奇数部分的整数倍。我们可以使用
    %
    检查整数倍条件,所以剩下的就是找到计算奇数部分的方法。在C或Python中,我会使用,扔掉指数,然后重复将分数乘以2,直到它成为整数,但是
    frexp
    在Java中似乎不可用。但是,Java确实有
    Math.getExponent
    ,它将提供
    frexp
    的指数部分,然后可以使用
    Math.scalb
    获得分数

  • 在计算
    x/y
    并得到一个(可能四舍五入的)结果
    z
    之后,您可以使用双-双算术(通过Veltkamp分裂和Dekker乘法)将
    y
    乘以
    z
    ,并检查结果是否完全等于
    x
    。这应该比使用
    BigDecimal
    的等效方法更有效,因为我们事先知道,包含结果不需要超过通常浮点精度两倍的精度

  • 恐怕我对Java不够流利,无法给出代码,但这里有一些Python代码,它应该可以直接适应Java。(请注意,Python的
    float
    类型与典型机器上Java的
    double
    类型相匹配;理论上,Python不需要IEEE 754,但在实践中,Python的
    float
    格式几乎不可避免地将是IEEE 754二进制64。)

    如果有人想窃取这段代码,将其转换为Java,并将其转化为答案,我将很乐意投票

    import math
    
    def odd_part(x):
        """
        Return an odd integer m (as a double) such that x can be written
        in the form m * 2**e for some exponent e. The exponent e is not
        returned.
        """
        fraction, exponent = math.frexp(x)
    
        # here fraction * 2**53 is guaranteed to be an integer, so we
        # don't need to loop more than 53 times.
        while fraction % 1.0 != 0.0:  # or in Python, use the is_integer method.
            fraction *= 2.0
        return fraction
    
    
    # Constant used in Veltkamp splitting.
    C = float.fromhex('0x1.0000002000000p+27')
    
    def split(x):
        """
        Split a double x into pieces x_hi, x_lo, each
        expressible with 26 bits of precision.
    
        Algorithm due to Veltkamp.
    
        Parameters
        ----------
        x : float
            Finite float, such that C*x does not overflow. Assumes IEEE 754
            representation and arithmetic, with round-ties-to-even rounding
            mode.
    
        Returns
        -------
        l, h : float
            l and h are both representable in 26 bits of precision, and
            x = l + h.
    
        """
        # Idea of proof: without loss of generality, we can reduce to the case
        # where 0.5 < x < 1 (the case where x is a power of 2 is straightforward).
        # Write rnd for the floating-point rounding operation, so p = rnd(Cx) and q
        # = rnd(x-p).
        #
        # Now let e and f be the errors induced by the floating-point operations,
        # so
        #     p = Cx + e
        #     q = x - p + f
        #
        # Then it's easy to show that:
        #
        #  2**26 < |Cx| < 2**28, so p is a multiple of 2**-26 and |e| <= 2**-26.
        #  2**26 <= p - x <= 2**27, so q is a multiple of 2**-26 and |f| <= 2**-27.
        #  h = p + q is exactly representable, equal to x + f
        #  h <= 1, and h is a multiple of 2**-26, so h has precision <= 26.
        #  l = x - h is exactly representable, equal to f.
        #  |f| <= 2**-27, and f is a multiple of 2**-53, so f has precision <= 26.
    
        p = C * x
        q = x - p
        h = p + q
        l = x - h
        return l, h
    
    
    def exact_mult(x, y):
        """
        Multiply floats x and y exactly, expressing the result as l + h,
        where h is the closest float to x * y and l is the error.
    
        Algorithm is due to Dekker.
    
        Assumes that x and y are finite IEEE 754 binary64 floats.
    
        May return inf or nan due to intermediate overflow.
    
        May raise ValueError on underflow or near-underflow.
    
        If both results are finite, then we have equality:
    
           x * y = h + l
    
        """
        # Write |x| = M * 2**e, y = |N| * 2**f, for some M and N with
        # M, N <= 2**53 - 1. Then xy = M*N*2**(e+f). If e + f <= -1075
        # then xy < (2**53 - 1)**2 * 2**-1075 < 2**-969 (1 - 2**-53),
        # which is exactly representable.
        # Hence the rounded value of |xy| is also < 2**-969.
    
        # So if |xy| >= 2**-969, and |xy| isn't near overflow, it follows that x*y
        # *can* be expressed as the sum of two doubles: 
    
        # If |xy| < 2**-969, we can't guarantee it, and we raise ValueError.
    
        h = x * y
    
        if abs(h) < 2**-969 and x != 0.0 and y != 0.0:
            raise ValueError("Cannot guarantee exact result.")
    
        xl, xh = split(x)
        yl, yh = split(y)
        return -h + xh * yh + xh * yl + xl * yh + xl * yl, h
    
    
    def division_exact_method_1(x, y):
        """
        Given nonzero finite not-too-large not-too-small floats x and y,
        return True if x / y is exactly representable, else False.
        """
        return odd_part(x) % odd_part(y) == 0
    
    
    def division_exact_method_2(x, y):
        """
        Given nonzero finite not-too-large not-too-small floats x and y,
        return True if x / y is exactly representable, else False.
        """
        z = x / y
        low, high = exact_mult(y, z)
        return high == x and low == 0
    
    导入数学
    def奇数_零件(x):
    """
    返回一个奇数整数m(作为双精度),以便可以写入x
    对于某些指数e,其形式为m*2**e。指数e不是
    返回。
    """
    分数,指数=math.frexp(x)
    #这里分数*2**53保证是整数,所以我们
    #不需要循环超过53次。
    而分数%1.0!=0.0:#或者在Python中,使用is_integer方法。
    分数*=2.0
    返回分数
    #用于Veltkamp拆分的常数。
    C=浮点.fromhex('0x1.000000200000p+27')
    def拆分(x):
    """
    把一个双x分成几块x_嗨,x_