2727 * then applying Frame of Reference encoding and bit-packing.
2828 * Values that cannot be losslessly converted are stored as exceptions.
2929 *
30- * <p>Encoding formula: encoded = round(value * 10^(exponent - factor))
31- * <p>Decoding formula: value = encoded / 10^(exponent - factor)
30+ * <p>Encoding formula: encoded = fastRound(value * POW10[e] * POW10_NEGATIVE[f])
31+ * <p>Decoding formula: value = encoded * POW10[f] * POW10_NEGATIVE[e]
32+ *
33+ * <p>The order of operations is critical for IEEE 754 correctness. Both formulas must
34+ * be evaluated as single expressions — storing the intermediate multiplication result
35+ * in a variable before the second multiply changes IEEE 754 rounding and produces extra
36+ * exceptions. Uses multiply-by-reciprocal (via POW10_NEGATIVE) for C++ wire compatibility.
3237 *
3338 * <p>Exception conditions:
3439 * <ul>
4146 */
4247final class AlpEncoderDecoder {
4348
49+ private static final double ENCODING_UPPER_LIMIT = 9223372036854774784.0 ;
50+ private static final double ENCODING_LOWER_LIMIT = -9223372036854774784.0 ;
51+ private static final float FLOAT_ENCODING_UPPER_LIMIT = 2147483520.0f ;
52+ private static final float FLOAT_ENCODING_LOWER_LIMIT = -2147483520.0f ;
53+
4454 private AlpEncoderDecoder () {
4555 // Utility class
4656 }
4757
48- static float getFloatMultiplier (int exponent , int factor ) {
49- float multiplier = FLOAT_POW10 [exponent ];
50- if (factor > 0 ) {
51- multiplier /= FLOAT_POW10 [factor ];
52- }
53- return multiplier ;
54- }
55-
56- static double getDoubleMultiplier (int exponent , int factor ) {
57- double multiplier = DOUBLE_POW10 [exponent ];
58- if (factor > 0 ) {
59- multiplier /= DOUBLE_POW10 [factor ];
60- }
61- return multiplier ;
62- }
63-
6458 /** NaN, Inf, and -0.0 can never be encoded regardless of exponent/factor. */
6559 static boolean isFloatException (float value ) {
6660 if (Float .isNaN (value )) {
@@ -77,27 +71,17 @@ static boolean isFloatException(float value, int exponent, int factor) {
7771 if (isFloatException (value )) {
7872 return true ;
7973 }
80- float multiplier = getFloatMultiplier ( exponent , factor );
81- float scaled = value * multiplier ;
82- if (scaled > Integer . MAX_VALUE || scaled < Integer . MIN_VALUE ) {
74+ // Check before rounding: overflow or non-finite after scaling
75+ float scaled = value * FLOAT_POW10 [ exponent ] * FLOAT_POW10_NEGATIVE [ factor ] ;
76+ if (! Float . isFinite ( scaled ) || scaled > FLOAT_ENCODING_UPPER_LIMIT || scaled < FLOAT_ENCODING_LOWER_LIMIT ) {
8377 return true ;
8478 }
8579 int encoded = encodeFloat (value , exponent , factor );
8680 float decoded = decodeFloat (encoded , exponent , factor );
8781 return Float .floatToRawIntBits (value ) != Float .floatToRawIntBits (decoded );
8882 }
8983
90- /** Encode: round(value * 10^exponent / 10^factor) */
91- static int encodeFloat (float value , int exponent , int factor ) {
92- return fastRoundFloat (value * getFloatMultiplier (exponent , factor ));
93- }
94-
95- /** Decode: encoded / 10^exponent * 10^factor */
96- static float decodeFloat (int encoded , int exponent , int factor ) {
97- return encoded / getFloatMultiplier (exponent , factor );
98- }
99-
100- // Uses the 2^22+2^23 magic-number trick to round without branching on the FPU.
84+ /** Round float to nearest integer using magic-number trick with sign branching. */
10185 static int fastRoundFloat (float value ) {
10286 if (value >= 0 ) {
10387 return (int ) ((value + MAGIC_FLOAT ) - MAGIC_FLOAT );
@@ -106,6 +90,16 @@ static int fastRoundFloat(float value) {
10690 }
10791 }
10892
93+ /** Encode: fastRound(value * POW10[e] * POW10_NEGATIVE[f]) — single expression. */
94+ static int encodeFloat (float value , int exponent , int factor ) {
95+ return fastRoundFloat (value * FLOAT_POW10 [exponent ] * FLOAT_POW10_NEGATIVE [factor ]);
96+ }
97+
98+ /** Decode: encoded * POW10[f] * POW10_NEGATIVE[e] — single expression. */
99+ static float decodeFloat (int encoded , int exponent , int factor ) {
100+ return encoded * FLOAT_POW10 [factor ] * FLOAT_POW10_NEGATIVE [exponent ];
101+ }
102+
109103 static boolean isDoubleException (double value ) {
110104 if (Double .isNaN (value )) {
111105 return true ;
@@ -120,25 +114,17 @@ static boolean isDoubleException(double value, int exponent, int factor) {
120114 if (isDoubleException (value )) {
121115 return true ;
122116 }
123- double multiplier = getDoubleMultiplier ( exponent , factor );
124- double scaled = value * multiplier ;
125- if (scaled > Long . MAX_VALUE || scaled < Long . MIN_VALUE ) {
117+ // Check before rounding: overflow or non-finite after scaling
118+ double scaled = value * DOUBLE_POW10 [ exponent ] * DOUBLE_POW10_NEGATIVE [ factor ] ;
119+ if (! Double . isFinite ( scaled ) || scaled > ENCODING_UPPER_LIMIT || scaled < ENCODING_LOWER_LIMIT ) {
126120 return true ;
127121 }
128122 long encoded = encodeDouble (value , exponent , factor );
129123 double decoded = decodeDouble (encoded , exponent , factor );
130124 return Double .doubleToRawLongBits (value ) != Double .doubleToRawLongBits (decoded );
131125 }
132126
133- static long encodeDouble (double value , int exponent , int factor ) {
134- return fastRoundDouble (value * getDoubleMultiplier (exponent , factor ));
135- }
136-
137- static double decodeDouble (long encoded , int exponent , int factor ) {
138- return encoded / getDoubleMultiplier (exponent , factor );
139- }
140-
141- // Same trick but with 2^51+2^52 for double precision.
127+ /** Round double to nearest integer using magic-number trick with sign branching. */
142128 static long fastRoundDouble (double value ) {
143129 if (value >= 0 ) {
144130 return (long ) ((value + MAGIC_DOUBLE ) - MAGIC_DOUBLE );
@@ -147,6 +133,16 @@ static long fastRoundDouble(double value) {
147133 }
148134 }
149135
136+ /** Encode: fastRound(value * POW10[e] * POW10_NEGATIVE[f]) — single expression. */
137+ static long encodeDouble (double value , int exponent , int factor ) {
138+ return fastRoundDouble (value * DOUBLE_POW10 [exponent ] * DOUBLE_POW10_NEGATIVE [factor ]);
139+ }
140+
141+ /** Decode: encoded * POW10[f] * POW10_NEGATIVE[e] — single expression. */
142+ static double decodeDouble (long encoded , int exponent , int factor ) {
143+ return encoded * DOUBLE_POW10 [factor ] * DOUBLE_POW10_NEGATIVE [exponent ];
144+ }
145+
150146 /** Number of bits needed to represent maxDelta as an unsigned value. */
151147 static int bitWidthForInt (int maxDelta ) {
152148 if (maxDelta == 0 ) {
0 commit comments