Java双精度浮点数平均值计算的数学特性及最优实现咨询
三个特性疑问解答
首先明确Java的double遵循IEEE 754双精度浮点规范,在你说明的x+y不会溢出的前提下,三个问题的结论如下:
- 针对问题1:结论成立。只要
x < y且二者之间存在至少一个可表示的浮点值,(x + y) / 2.0的结果必然严格落在x和y之间,即满足average > x && average < y。IEEE 754的加法运算默认采用就近舍入到偶数的模式,不溢出时x+y的误差不超过0.5ulp,除以2(2的幂次,运算精确无误差)后的结果必然不会超出(x,y)的区间范围。 - 针对问题2:结论成立。如果x和y是相邻的可表示浮点数,二者的平均值没有对应的精确可表示值,舍入后只会取x或y二者之一,因此恒满足
average == x || average == y。 - 针对问题3:除x/y为NaN的特殊情况外,结论成立。当x和y是合法数值且
x == y时,x + y = 2x,除以2的运算精确,结果必然等于x。
两种计算方式的优劣对比
(x + y) / 2.0和x / 2.0 + y / 2.0各有适用场景:
(x + y) / 2.0:不溢出的前提下精度更高,全程仅做1次舍入(加法运算),除以2是指数位右移,无精度损失。缺点是当x和y都是接近Double.MAX_VALUE的正数/接近Double.MIN_VALUE的负数时,x+y会溢出得到无穷大,结果错误。x / 2.0 + y / 2.0:不会出现溢出问题,哪怕x和y都是双精度极值也能得到正确结果。缺点是需要做3次舍入(两次除法、一次加法),精度比前者略低。
最值搜索场景的通用推荐实现
最值搜索场景通常要求平均值严格落在[x,y]区间内,且尽量避免溢出问题,推荐使用average = x + (y - x) / 2.0的实现:
- 该写法完全规避了x+y的溢出风险,只要x和y是同号的边界值(最值搜索的上下界通常满足这个条件),y-x的运算不会溢出
- 精度表现介于上述两种写法之间,且能保证结果始终落在[x,y]区间内,完全符合最值搜索的需求
- 如果你的JDK版本高于1.8,也可以直接使用标准库
Math.average(double a, double b),其内部实现已经做了溢出兼容和精度优化。
内容的提问来源于stack exchange,提问作者laneo
相关产品推荐
相关产品推荐

