1515 */
1616package com .google .common .geometry ;
1717
18+ import com .google .common .annotations .VisibleForTesting ;
1819import com .google .common .base .Preconditions ;
1920
20- public strictfp class S2 {
21- // declare some frequently use constants
21+ public final strictfp class S2 {
22+
23+ // Declare some frequently used constants
2224 public static final double M_PI = Math .PI ;
2325 public static final double M_1_PI = 1.0 / Math .PI ;
2426 public static final double M_PI_2 = Math .PI / 2.0 ;
@@ -35,6 +37,30 @@ public strictfp class S2 {
3537 public static final int SWAP_MASK = 0x01 ;
3638 public static final int INVERT_MASK = 0x02 ;
3739
40+ // Number of bits in the mantissa of a double.
41+ private static final int EXPONENT_SHIFT = 52 ;
42+ // Mask to extract the exponent from a double.
43+ private static final long EXPONENT_MASK = 0x7ff0000000000000L ;
44+
45+ /**
46+ * If v is non-zero, return an integer {@code exp} such that
47+ * {@code (0.5 <= |v|*2^(-exp) < 1)}. If v is zero, return 0.
48+ *
49+ * <p>Note that this arguably a bad definition of exponent because it makes
50+ * {@code exp(9) == 4}. In decimal this would be like saying that the exponent
51+ * of 1234 is 4, when in scientific 'exponent' notation 1234 is
52+ * {@code 1.234 x 10^3}.
53+ *
54+ * TODO(dbeaumont): Replace this with "DoubleUtils.getExponent(v) - 1" ?
55+ */
56+ @ VisibleForTesting
57+ static int exp (double v ) {
58+ if (v == 0 ) {
59+ return 0 ;
60+ }
61+ long bits = Double .doubleToLongBits (v );
62+ return (int ) ((EXPONENT_MASK & bits ) >> EXPONENT_SHIFT ) - 1022 ;
63+ }
3864
3965 /**
4066 * kPosToOrientation[pos] -> orientation_modifier
@@ -114,10 +140,10 @@ public int getMinLevel(double value) {
114140 }
115141
116142 // This code is equivalent to computing a floating-point "level"
117- // value and rounding up. frexp() returns a fraction in the
118- // range [0.5,1) and the corresponding exponent.
119- int level = DoubleMath . frexp ( value / (( 1 << dim ) * deriv )). exp ;
120- level = Math . max ( 0 , Math .min (S2CellId .MAX_LEVEL , -((level - 1 ) >> (dim - 1 ))));
143+ // value and rounding up.
144+ int exponent = exp ( value / (( 1 << dim ) * deriv ));
145+ int level = Math . max ( 0 ,
146+ Math .min (S2CellId .MAX_LEVEL , -((exponent - 1 ) >> (dim - 1 ))));
121147 // assert (level == S2CellId.MAX_LEVEL || getValue(level) <= value);
122148 // assert (level == 0 || getValue(level - 1) > value);
123149 return level ;
@@ -137,8 +163,9 @@ public int getMaxLevel(double value) {
137163
138164 // This code is equivalent to computing a floating-point "level"
139165 // value and rounding down.
140- int level = DoubleMath .frexp ((1 << dim ) * deriv / value ).exp ;
141- level = Math .max (0 , Math .min (S2CellId .MAX_LEVEL , (level - 1 ) >> (dim - 1 )));
166+ int exponent = exp ((1 << dim ) * deriv / value );
167+ int level = Math .max (0 ,
168+ Math .min (S2CellId .MAX_LEVEL , ((exponent - 1 ) >> (dim - 1 ))));
142169 // assert (level == 0 || getValue(level) >= value);
143170 // assert (level == S2CellId.MAX_LEVEL || getValue(level + 1) < value);
144171 return level ;
0 commit comments