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
的等效方法更有效,因为我们事先知道,包含结果不需要超过通常浮点精度两倍的精度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_