|
1 | 1 | /*
|
2 |
| - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
4 | 4 | *
|
5 | 5 | * The Universal Permissive License (UPL), Version 1.0
|
|
41 | 41 |
|
42 | 42 | package org.graalvm.wasm;
|
43 | 43 |
|
44 |
| -import com.oracle.truffle.api.ExactMath; |
45 |
| - |
46 | 44 | import static java.lang.Integer.compareUnsigned;
|
47 | 45 |
|
48 | 46 | /**
|
|
51 | 49 | */
|
52 | 50 | public final class WasmMath {
|
53 | 51 |
|
54 |
| - /** |
55 |
| - * The number of logical bits in the significand of a {@code double}, <strong>excluding</strong> |
56 |
| - * the implicit bit. |
57 |
| - */ |
58 |
| - private static final long DOUBLE_SIGNIFICAND_WIDTH = 52; |
59 |
| - |
60 |
| - /** |
61 |
| - * Bit mask to isolate the significand field of a <code>double</code>. |
62 |
| - */ |
63 |
| - public static final long DOUBLE_SIGNIFICAND_BIT_MASK = 0x000FFFFFFFFFFFFFL; |
64 |
| - |
65 |
| - /** |
66 |
| - * The spacing between two consecutive {@code float} values (aka Unit in the Last Place) in the |
67 |
| - * range [2^31, 2^32): 2^40. |
68 |
| - */ |
69 |
| - private static final long FLOAT_POWER_63_ULP = (long) Math.ulp(0x1p63f); |
70 |
| - |
71 |
| - /** |
72 |
| - * The spacing between two consecutive {@code double} values (aka Unit in the Last Place) in the |
73 |
| - * range [2^63, 2^64): 2^11. |
74 |
| - */ |
75 |
| - private static final long DOUBLE_POWER_63_ULP = (long) Math.ulp(0x1p63); |
76 |
| - |
77 | 52 | /**
|
78 | 53 | * Don't let anyone instantiate this class.
|
79 | 54 | */
|
@@ -130,147 +105,14 @@ public static int toUnsignedIntExact(long value) {
|
130 | 105 | * Converts the given unsigned {@code int} to the closest {@code float} value.
|
131 | 106 | */
|
132 | 107 | public static float unsignedIntToFloat(int x) {
|
133 |
| - return unsignedIntToLong(x); |
| 108 | + return Integer.toUnsignedLong(x); |
134 | 109 | }
|
135 | 110 |
|
136 | 111 | /**
|
137 | 112 | * Converts the given unsigned {@code int} to the closest {@code double} value.
|
138 | 113 | */
|
139 | 114 | public static double unsignedIntToDouble(int x) {
|
140 |
| - return unsignedIntToLong(x); |
141 |
| - } |
142 |
| - |
143 |
| - /** |
144 |
| - * Converts the given unsigned {@code int} to the corresponding {@code long}. |
145 |
| - */ |
146 |
| - public static long unsignedIntToLong(int x) { |
147 |
| - // See https://stackoverflow.com/a/22938125. |
148 |
| - return x & 0xFFFFFFFFL; |
149 |
| - } |
150 |
| - |
151 |
| - /** |
152 |
| - * Converts the given unsigned {@code long} to the closest {@code float} value. |
153 |
| - */ |
154 |
| - public static float unsignedLongToFloat(long x) { |
155 |
| - if (x >= 0) { |
156 |
| - // If the first bit is not set, then we can simply cast which is faster. |
157 |
| - return x; |
158 |
| - } |
159 |
| - |
160 |
| - // Transpose x from [Integer.MIN_VALUE,-1] to [0, Integer.MAX_VALUE] |
161 |
| - final long shiftedX = x + Long.MIN_VALUE; |
162 |
| - |
163 |
| - // We cannot simply compute 0x1p63f + shiftedX because it yields incorrect results in some |
164 |
| - // edge cases due to rounding twice (conversion to long, and addition). See |
165 |
| - // https://github.com/WebAssembly/spec/issues/421 and mentioned test cases for more context. |
166 |
| - // Instead, we manually compute the offset from 0x1p63f. |
167 |
| - final boolean roundUp = shiftedX % FLOAT_POWER_63_ULP > FLOAT_POWER_63_ULP / 2; |
168 |
| - final long offset = (shiftedX / FLOAT_POWER_63_ULP) + (roundUp ? 1 : 0); |
169 |
| - |
170 |
| - // Return the offset-nth next floating-point value starting from 2^63. This is equivalent to |
171 |
| - // incrementing the significand (as Math.nextUp would do) offset times. |
172 |
| - return 0x1p63f + (offset * (float) FLOAT_POWER_63_ULP); |
173 |
| - } |
174 |
| - |
175 |
| - /** |
176 |
| - * Converts the given unsigned {@code long} to the closest {@code double} value. |
177 |
| - */ |
178 |
| - public static double unsignedLongToDouble(long x) { |
179 |
| - if (x >= 0) { |
180 |
| - // If the first bit is not set, then we can simply cast which is faster. |
181 |
| - return x; |
182 |
| - } |
183 |
| - |
184 |
| - // Transpose x from [Long.MIN_VALUE,-1] to [0, Long.MAX_VALUE]. |
185 |
| - final long shiftedX = x + Long.MIN_VALUE; |
186 |
| - |
187 |
| - // We cannot simply compute 0x1p63 + shiftedX because it yields incorrect results in some |
188 |
| - // edge cases due to rounding twice (conversion to long, and addition). See |
189 |
| - // https://github.com/WebAssembly/spec/issues/421 and mentioned test cases for more context. |
190 |
| - // Instead, we manually compute the offset from 0x1p63. |
191 |
| - final boolean roundUp = shiftedX % DOUBLE_POWER_63_ULP > DOUBLE_POWER_63_ULP / 2; |
192 |
| - final long offset = (shiftedX / DOUBLE_POWER_63_ULP) + (roundUp ? 1 : 0); |
193 |
| - |
194 |
| - // Return the offset-nth next floating-point value starting form 2^63. This is equivalent to |
195 |
| - // incrementing the significand (as Math.nextUp would do) offset times. |
196 |
| - return 0x1p63 + (offset * (double) DOUBLE_POWER_63_ULP); |
197 |
| - } |
198 |
| - |
199 |
| - /** |
200 |
| - * Removes the decimal part (aka truncation or rounds towards zero) of the given float and |
201 |
| - * converts it to a <strong>signed</strong> long. |
202 |
| - * <p> |
203 |
| - * The operation is saturating: if the float is smaller than {@link Integer#MIN_VALUE} or larger |
204 |
| - * than {@link Integer#MAX_VALUE}, then respectively {@link Integer#MIN_VALUE} or |
205 |
| - * {@link Integer#MAX_VALUE} is returned. |
206 |
| - */ |
207 |
| - public static long truncFloatToLong(float x) { |
208 |
| - return truncDoubleToLong(x); |
209 |
| - } |
210 |
| - |
211 |
| - /** |
212 |
| - * Removes the decimal part (aka truncation or rounds towards zero) of the given double and |
213 |
| - * converts it to a <strong>signed</strong> long. |
214 |
| - * <p> |
215 |
| - * The operation is saturating: if the double is smaller than {@link Long#MIN_VALUE} or larger |
216 |
| - * than {@link Long#MAX_VALUE}, then respectively {@link Long#MIN_VALUE} or |
217 |
| - * {@link Long#MAX_VALUE} is returned. |
218 |
| - */ |
219 |
| - public static long truncDoubleToLong(double x) { |
220 |
| - return (long) ExactMath.truncate(x); |
221 |
| - } |
222 |
| - |
223 |
| - /** |
224 |
| - * Removes the decimal part (aka truncation or rounds towards zero) of the given float and |
225 |
| - * converts it to an <strong>unsigned</strong> long. |
226 |
| - * <p> |
227 |
| - * The operation is saturating: if the float is smaller than 0 or larger than 2^32 - 1, then |
228 |
| - * respectively 0 or 2^32 - 1 is returned. |
229 |
| - */ |
230 |
| - public static long truncFloatToUnsignedLong(float x) { |
231 |
| - return truncDoubleToUnsignedLong(x); |
232 |
| - } |
233 |
| - |
234 |
| - /** |
235 |
| - * Removes the decimal part (aka truncation or rounds towards zero) of the given double and |
236 |
| - * converts it to an <strong>unsigned</strong> long. |
237 |
| - * <p> |
238 |
| - * The operation is saturating: if the double is smaller than 0 or larger than 2^64 - 1, then |
239 |
| - * respectively 0 or 2^64 - 1 is returned. |
240 |
| - */ |
241 |
| - public static long truncDoubleToUnsignedLong(double x) { |
242 |
| - if (x < Long.MAX_VALUE) { |
243 |
| - // If the first bit is not set, then we use the signed variant which is faster. |
244 |
| - return truncDoubleToLong(x); |
245 |
| - } |
246 |
| - |
247 |
| - // There is no direct way to convert a double to an _unsigned_ long in Java. Therefore we |
248 |
| - // manually split the binary representation of x into significand (aka base or mantissa) and |
249 |
| - // exponent, and compute the resulting long by shifting the significand. |
250 |
| - |
251 |
| - final long shift = Math.getExponent(x) - DOUBLE_SIGNIFICAND_WIDTH; |
252 |
| - final long xBits = Double.doubleToRawLongBits(x); |
253 |
| - final long significand = (1L << DOUBLE_SIGNIFICAND_WIDTH) | (xBits & DOUBLE_SIGNIFICAND_BIT_MASK); |
254 |
| - |
255 |
| - if (shift >= Long.SIZE - DOUBLE_SIGNIFICAND_WIDTH) { |
256 |
| - // Saturation: if x is too large to convert to a long, we return the highest possible |
257 |
| - // value (all bits set). |
258 |
| - return 0xffff_ffff_ffff_ffffL; |
259 |
| - } else if (shift > 0) { |
260 |
| - // Multiply significand by 2^shift. |
261 |
| - return significand << shift; |
262 |
| - } |
263 |
| - |
264 |
| - // Should not reach here because x >= Long.MAX_VALUE, so shift >= |
265 |
| - // (Math.getExponent(Long.MAX_VALUE) - DOUBLE_SIGNIFICAND_WIDTH) == 11. |
266 |
| - |
267 |
| - if (shift >= -DOUBLE_SIGNIFICAND_WIDTH) { |
268 |
| - // Multiply significand by 2^shift == divide significand by 2^(-shift). |
269 |
| - return significand >> -shift; |
270 |
| - } else { |
271 |
| - // Saturation: if x is too small to convert to a long, we return 0. |
272 |
| - return 0; |
273 |
| - } |
| 115 | + return Integer.toUnsignedLong(x); |
274 | 116 | }
|
275 | 117 |
|
276 | 118 | }
|
0 commit comments