简体   繁体   English

Java-通过转换为双精度字长查找前导零

[英]Java - finding leading zeros in a long by conversion to double

Prior to finding the method Long.numberOfLeadingZeros(long i) , I was casting longs to doubles and using Math.getExponent(double d) . 在找到方法Long.numberOfLeadingZeros(long i)之前 ,我将long转换为double并使用Math.getExponent(double d) The idea was to find the double representation of the long, use the exponent to get the highest set bit, and subtract it from 64 to get the number of leading zeros. 想法是找到long的双精度表示形式,使用指数获得最高的置位比特,然后从64中减去它以获得前导零的数目。

This mostly worked, but was occasionally off by 1. The following for-loop was used to highlight the problem: 这主要起作用,但偶尔会被1淘汰。以下for循环用于突出显示问题:

for (int i = 0; i < 64; i++) {
    double max = Long.MAX_VALUE >>> i;
    double min = Long.MIN_VALUE >>> i;
    double neg = -1L >>> i;
    System.out.format("Max: %-5d Min: %-5d -1: %-5d%n", Math.getExponent(dmax),
                                Math.getExponent(dmin), Math.getExponent(dneg));
}

with the significant portion of output: 输出的重要部分:

...
Max: 55    Min: 55    -1: 56   
Max: 54    Min: 54    -1: 55   
Max: 52    Min: 53    -1: 54   
Max: 51    Min: 52    -1: 52   
Max: 50    Min: 51    -1: 51
...  

The longs with all bits set are off by 1 above 2^52. 所有位都设置为1的多头在2 ^ 52以上偏移1。 As this post explains, a loss of precision occurs due to the storage of 53+ significant bits in the 52-bit mantissa. 正如这篇文章所解释的,由于在52位尾数中存储了53+个有效位,因此会导致精度损失。 However, I am struggling to understand why the exponent is affected. 但是,我在努力理解为什么指数受到影响。

While I am no longer using this method, I am still curious: why and under what circumstances does this method of finding the leading zeros in a long fail? 尽管我不再使用这种方法,但我仍然感到好奇:为什么这种在长途失败中找到前导零的方法会在什么情况下出现?

The limit of precision on a double forces the binary representation to round up to the nearest power of 2 , which increments the exponent in the floating-point representation of the double value. double的精度限制会强制二进制表示舍入到最接近的2的幂,这会增加double值的浮点表示中的指数。 This occurs because a double's mantissa, including the implied 1 bit, is 53 bits, but a long has 64 bits. 发生这种情况是因为double的尾数(包括隐含的1位)为53位,而long的尾数为64位。

Section 5.1.2 of the JLS covers what may happen in this widening primitive conversion: JLS的5.1.2节介绍了这种扩展的原始转换可能发生的情况:

A widening primitive conversion from int to float, or from long to float, or from long to double, may result in loss of precision - that is, the result may lose some of the least significant bits of the value. 从int到float或从long到float或从long到double的原始转换范围扩大,可能会导致精度损失-也就是说,结果可能会丢失该值的某些最低有效位。 In this case, the resulting floating-point value will be a correctly rounded version of the integer value , using IEEE 754 round-to-nearest mode (§4.2.4). 在这种情况下,使用IEEE 754舍入到最近模式(第4.2.4节),结果浮点值将是整数值正确舍入版本

(emphasis mine) (强调我的)

Here, I use Double.doubleToLongBits to preserve the bits of a double in a long , and Long.toHexString to print out the hex values of the original double s. 在这里,我使用Double.doubleToLongBits将一个double的位保留在long ,并使用Long.toHexString打印出原始double的十六进制值。

System.out.format("Max(%s): %-5d Min(%s): %-5d -1(%s): %-5d%n",
                Long.toHexString(Double.doubleToLongBits(dmax)), Math.getExponent(dmax),
                Long.toHexString(Double.doubleToLongBits(dmax)), Math.getExponent(dmin),
                Long.toHexString(Double.doubleToLongBits(dneg)), Math.getExponent(dneg));

Output: 输出:

Max(43e0000000000000): 63    Min(43e0000000000000): 63    -1(bff0000000000000): 0    
Max(43d0000000000000): 62    Min(43d0000000000000): 62    -1(43e0000000000000): 63   
Max(43c0000000000000): 61    Min(43c0000000000000): 61    -1(43d0000000000000): 62   
Max(43b0000000000000): 60    Min(43b0000000000000): 60    -1(43c0000000000000): 61   
Max(43a0000000000000): 59    Min(43a0000000000000): 59    -1(43b0000000000000): 60   
Max(4390000000000000): 58    Min(4390000000000000): 58    -1(43a0000000000000): 59   
Max(4380000000000000): 57    Min(4380000000000000): 57    -1(4390000000000000): 58   
Max(4370000000000000): 56    Min(4370000000000000): 56    -1(4380000000000000): 57   
Max(4360000000000000): 55    Min(4360000000000000): 55    -1(4370000000000000): 56   
Max(4350000000000000): 54    Min(4350000000000000): 54    -1(4360000000000000): 55   
Max(433fffffffffffff): 52    Min(433fffffffffffff): 53    -1(4350000000000000): 54   
Max(432ffffffffffffe): 51    Min(432ffffffffffffe): 52    -1(433fffffffffffff): 52   

The original long values with more than 53 1 bits are rounded up when converted to a double , losing precision. 大于53 1位的原始long值在转换为double时会四舍五入,从而失去精度。 The exponent field consists of bits 2 through 12 , visible in the 1st 3 hex digits printed out above. 指数字段由位212组成,这些位在上面打印的前3个十六进制数字中可见。

When the value is shifted below 53 1 bits, double 's precision is now sufficient to hold the value without rounding (the round up is no longer necessary) and the mantissa's bits become visible as 'f' hex digits. 当值移到53 1位以下时, double的精度现在足以保持该值而无需舍入(不再需要舍入),尾数的位变为可见的“ f”十六进制数字。 There is a discontinuity in the exponent field, going from 435 to 433 , explaining why there is a discontinuity in the result of Math.getExponent , from 54 to 52 . 指数字段从435433不连续,这说明了Math.getExponent的结果为什么从5452不连续。

A number with all ones, when rounded to fewer places will be rounded up and therefore become one bit longer. 一个全为1的数字会四舍五入,因此会加长一位。 For example (assume a double has only 5 bits in the mantissa) 例如(假设双精度数的尾数只有5位)

111111 becomes 1000000

when rounded. 四舍五入时。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM