Skip to content

Commit 4446eb0

Browse files
committed
Add tests for ExactMath unsigned integer from/to float conversion methods.
1 parent 070bdaa commit 4446eb0

File tree

1 file changed

+340
-0
lines changed

1 file changed

+340
-0
lines changed
Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.truffle.api.test.utilities;
42+
43+
import static java.util.Map.entry;
44+
45+
import java.util.Collection;
46+
import java.util.List;
47+
import java.util.Map;
48+
import java.util.stream.Stream;
49+
50+
import org.junit.Assert;
51+
import org.junit.Test;
52+
53+
import com.oracle.truffle.api.ExactMath;
54+
55+
/**
56+
* Tests unsigned integer from and to floating point conversion methods.
57+
*
58+
* @see ExactMath
59+
*/
60+
public class UnsignedFloatConvertTest {
61+
62+
/**
63+
* Signaling NaN values.
64+
*/
65+
static final double DOUBLE_SNAN = Double.longBitsToDouble(0x7ff0000000000001L);
66+
static final float FLOAT_SNAN = Float.intBitsToFloat(0x7f800001);
67+
68+
private static long minUnsigned(long a, long b) {
69+
return Long.compareUnsigned(a, b) < 0 ? a : b;
70+
}
71+
72+
/**
73+
* Clamps uint64 values to uint32 range.
74+
*/
75+
private static int clampU64ToU32(long value) {
76+
return (int) minUnsigned(value, 0xffff_ffffL);
77+
}
78+
79+
/**
80+
* Intersperse values with flipped sign bit.
81+
*/
82+
@SuppressWarnings("unchecked")
83+
private static <T extends Number> Iterable<Map.Entry<T, Long>> withNegatives(Collection<Map.Entry<T, Long>> positives) {
84+
return () -> positives.stream().flatMap(e -> Stream.of(e,
85+
entry((T) (e.getKey() instanceof Double d ? Double.longBitsToDouble(Double.doubleToRawLongBits(d) ^ (1L << 63))
86+
: e.getKey() instanceof Float f ? Float.intBitsToFloat(Float.floatToRawIntBits(f) ^ (1 << 31))
87+
: e.getKey()),
88+
0L))).iterator();
89+
}
90+
91+
/**
92+
* @see ExactMath#truncateToUnsignedInt(float)
93+
* @see ExactMath#truncateToUnsignedLong(float)
94+
*/
95+
@Test
96+
public void floatToUnsignedInteger() {
97+
for (var pair : withNegatives(List.of(
98+
entry(0.0f, 0L),
99+
100+
entry(Float.NaN, 0L),
101+
entry(FLOAT_SNAN, 0L),
102+
103+
entry(Float.POSITIVE_INFINITY, ~0L),
104+
105+
entry(0x1p-149f, 0L),
106+
entry(0x1.ccccccp-1f, 0L),
107+
entry(0x1.fffffep-1f, 0L),
108+
109+
entry(1.0f, 1L),
110+
entry(1.1f, 1L),
111+
entry(1.5f, 1L),
112+
entry(1.9f, 1L),
113+
entry(2.0f, 2L),
114+
115+
entry(0x1.fffffep+31f, 0xffffff00L),
116+
entry(0x1.0p+31f, 1L << 31),
117+
entry(0x1.0p+32f, 1L << 32),
118+
119+
entry(1e+8f, 100_000_000L),
120+
entry(1e+16f, 10_000_000_272_564_224L),
121+
122+
entry(0x1.fffffcp+22f, 0x00000000_007fffffL),
123+
// largest representable non-integral value (0x1p23 - 0.5)
124+
entry(0x1.fffffep+22f, 0x00000000_007fffffL),
125+
entry(0x1.000000p+23f, 0x00000000_00800000L),
126+
entry(0x1.000002p+23f, 0x00000000_00800001L),
127+
128+
entry(0x1.000000p+24f, 0x00000000_01000000L),
129+
entry(0x1.000002p+24f, 0x00000000_01000002L),
130+
entry(0x1.000004p+24f, 0x00000000_01000004L),
131+
132+
entry(0x1.000000p+53f, 0x00200000_00000000L),
133+
entry(0x1.000001p+53f, 0x00200000_00000000L),
134+
entry(0x1.000002p+53f, 0x00200000_40000000L),
135+
entry(0x1.fffffep+62f, 0x7fffff80_00000000L),
136+
entry(0x1.000000p+63f, Long.MIN_VALUE),
137+
entry(0x1.000002p+63f, 0x80000100_00000000L),
138+
entry(0x1.ffc000p+63f, 0xffe00000_00000000L),
139+
entry(0x1.fffffcp+63f, 0xfffffe00_00000000L),
140+
entry(0x1.fffffep+63f, 0xffffff00_00000000L),
141+
142+
entry(0x1.0p+64f, ~0L),
143+
entry(Float.MAX_VALUE, ~0L)))) {
144+
final float x = pair.getKey();
145+
long expected = pair.getValue();
146+
long actual = ExactMath.truncateToUnsignedLong(x);
147+
148+
Assert.assertEquals("truncateFloatToUnsignedLong(%s, %s) = expected: 0x%s, actual: 0x%s".formatted(x, Float.toHexString(x),
149+
Long.toUnsignedString(expected, 16), Long.toUnsignedString(actual, 16)),
150+
expected, actual);
151+
152+
int expectedInt = clampU64ToU32(expected);
153+
int actualInt = ExactMath.truncateToUnsignedInt(x);
154+
155+
Assert.assertEquals("truncateFloatToUnsignedInt(%s, %s) = expected: 0x%s, actual: 0x%s".formatted(x, Float.toHexString(x),
156+
Integer.toUnsignedString(expectedInt, 16), Integer.toUnsignedString(actualInt, 16)),
157+
expectedInt, actualInt);
158+
}
159+
}
160+
161+
/**
162+
* @see ExactMath#truncateToUnsignedInt(double)
163+
* @see ExactMath#truncateToUnsignedLong(double)
164+
*/
165+
@Test
166+
public void doubleToUnsignedInteger() {
167+
for (Map.Entry<Double, Long> pair : withNegatives(List.of(
168+
entry(0.0, 0L),
169+
170+
entry(Double.NaN, 0L),
171+
entry(DOUBLE_SNAN, 0L),
172+
173+
entry(Double.POSITIVE_INFINITY, ~0L),
174+
175+
entry(0x1p-149, 0L),
176+
entry(0x1.ccccccccccccdp-1, 0L),
177+
entry(0x1.fffffffffffffp-1, 0L),
178+
179+
entry(1.0, 1L),
180+
entry(1.1, 1L),
181+
entry(1.5, 1L),
182+
entry(1.9, 1L),
183+
entry(2.0, 2L),
184+
185+
entry(1e+8, 100_000_000L),
186+
entry(1e+16, 10_000_000_000_000_000L),
187+
188+
entry(0x1.0p+31, 1L << 31),
189+
entry(0x1.fffffe0000000p+31, 0x00000000_ffffff00L),
190+
entry(0x1.fffffffc00000p+31, 0x00000000_fffffffeL),
191+
entry(0x1.ffffffffffffep+31, 0x00000000_ffffffffL),
192+
entry(0x1.fffffffffffffp+31, 0x00000000_ffffffffL),
193+
entry(0x1.0p+32, 1L << 32),
194+
195+
entry(0x1.ffffffffffffep+51, 0x000fffff_ffffffffL),
196+
// largest representable non-integral value (0x1p52 - 0.5)
197+
entry(0x1.fffffffffffffp+51, 0x000fffff_ffffffffL),
198+
entry(0x1.0000000000000p+52, 0x00100000_00000000L),
199+
200+
entry(0x1.0000000000000p+53, 0x00200000_00000000L),
201+
entry(0x1.0000000000001p+53, 0x00200000_00000002L),
202+
entry(0x1.0000000000002p+53, 0x00200000_00000004L),
203+
entry(0x1.0000010000000p+53, 0x00200000_20000000L),
204+
entry(0x1.0000020000000p+53, 0x00200000_40000000L),
205+
entry(0x1.fffffe0000000p+62, 0x7fffff80_00000000L),
206+
entry(0x1.fffffffffffffp+62, 0x7fffffff_fffffc00L),
207+
entry(0x1.0000000000000p+63, Long.MIN_VALUE),
208+
entry(0x1.0000000000001p+63, 0x80000000_00000800L),
209+
entry(0x1.0000000000002p+63, 0x80000000_00001000L),
210+
entry(0x1.0000020000000p+63, 0x80000100_00000000L),
211+
entry(0x1.ffc0000000000p+63, 0xffe00000_00000000L),
212+
entry(0x1.fffffe0000000p+63, 0xffffff00_00000000L),
213+
entry(0x1.fffffffffffffp+63, 0xffffffff_fffff800L),
214+
215+
entry(0x1.0p+64, ~0L),
216+
entry((double) Float.MAX_VALUE, ~0L),
217+
entry(Double.MAX_VALUE, ~0L)))) {
218+
final double x = pair.getKey();
219+
long expected = pair.getValue();
220+
long actual = ExactMath.truncateToUnsignedLong(x);
221+
Assert.assertEquals("truncateDoubleToUnsignedLong(%s, %s) = expected: 0x%s, actual: 0x%s".formatted(x, Double.toHexString(x),
222+
Long.toUnsignedString(expected, 16), Long.toUnsignedString(actual, 16)),
223+
expected, actual);
224+
225+
int expectedInt = clampU64ToU32(expected);
226+
int actualInt = ExactMath.truncateToUnsignedInt(x);
227+
Assert.assertEquals("truncateDoubleToUnsignedInt(%s, %s) = expected: 0x%s, actual: 0x%s".formatted(x, Double.toHexString(x),
228+
Integer.toUnsignedString(expectedInt, 16), Integer.toUnsignedString(actualInt, 16)),
229+
expectedInt, actualInt);
230+
}
231+
}
232+
233+
/**
234+
* @see ExactMath#unsignedToFloat(long)
235+
*/
236+
@Test
237+
public void unsignedToFloat() {
238+
for (var pair : List.of(
239+
entry(0L, 0.0f),
240+
entry(1L, 1.0f),
241+
242+
entry(0x00000000_007fffffL, 0x1.fffffcp+22f),
243+
entry(0x00000000_00800000L, 0x1.0p+23f),
244+
245+
entry(0x00000000_01000000L, 0x1.0p+24f),
246+
entry(0x00000000_01000001L, 0x1.0p+24f),
247+
entry(0x00000000_01000002L, 0x1.000002p+24f),
248+
entry(0x00000000_01000003L, 0x1.000004p+24f),
249+
250+
entry(0x00000000_ffffff00L, 0x1.fffffep+31f),
251+
entry(0x00000000_ffffff7fL, 0x1.fffffep+31f),
252+
entry(0x00000000_ffffff80L, 0x1.0p+32f),
253+
entry(0x00000000_ffffffd1L, 0x1.0p+32f),
254+
entry(0x00000001_00000000L, 0x1.0p+32f),
255+
256+
entry(0x00200000_00000000L, 0x1.0p+53f),
257+
entry(0x00200000_20000000L, 0x1.0p+53f),
258+
entry(0x00200000_20000001L, 0x1.000002p+53f),
259+
260+
entry(0x7fffff40_00000000L, 0x1.fffffcp+62f),
261+
entry(0x7fffff40_00000001L, 0x1.fffffep+62f),
262+
entry(0x7fffffbf_ffffffffL, 0x1.fffffep+62f),
263+
entry(0x7fffffc0_00000000L, 0x1.0p+63f),
264+
entry(0x7fffffff_ffffffffL, 0x1.0p+63f), // Long.MAX_VALUE
265+
entry(0x80000000_00000000L, 0x1.0p+63f), // Long.MIN_VALUE
266+
entry(0x80000080_00000000L, 0x1.0p+63f),
267+
entry(0x80000080_00000001L, 0x1.000002p+63f),
268+
entry(0x80000100_00000000L, 0x1.000002p+63f),
269+
entry(0xffdfffff_dfffffffL, 0x1.ffc000p+63f),
270+
entry(0xfffffe80_00000000L, 0x1.fffffcp+63f),
271+
entry(0xfffffe80_00000001L, 0x1.fffffep+63f),
272+
entry(0xfffffe80_00000002L, 0x1.fffffep+63f),
273+
entry(0xffffff00_00000000L, 0x1.fffffep+63f),
274+
entry(0xffffff7f_ffffffffL, 0x1.fffffep+63f),
275+
entry(0xffffff80_00000000L, 0x1.0p+64f),
276+
entry(0xffffffff_dfffffffL, 0x1.0p+64f),
277+
entry(0xffffffff_ffffffffL, 0x1.0p+64f))) {
278+
final long x = pair.getKey();
279+
float expected = pair.getValue();
280+
float actual = ExactMath.unsignedToFloat(x);
281+
Assert.assertEquals("unsignedLongToFloat(%s, 0x%s) = expected: %s, actual: %s".formatted(
282+
Long.toUnsignedString(x), Long.toUnsignedString(x, 16),
283+
Float.toHexString(expected), Float.toHexString(actual)),
284+
expected, actual, 0.0f);
285+
}
286+
}
287+
288+
/**
289+
* @see ExactMath#unsignedToDouble(long)
290+
*/
291+
@Test
292+
public void unsignedToDouble() {
293+
for (var pair : List.of(
294+
entry(0L, 0.0),
295+
entry(1L, 1.0),
296+
297+
entry(0x00000000_007fffffL, 0x1.fffffcp+22),
298+
entry(0x00000000_00800000L, 0x1.0p+23),
299+
300+
entry(0x00000000_fffffffeL, 0x1.fffffffcp+31),
301+
entry(0x00000000_ffffffffL, 0x1.fffffffep+31),
302+
entry(0x00000001_00000000L, 0x1.0p+32),
303+
304+
entry(0x000fffff_ffffffffL, 0x1.ffffffffffffep+51),
305+
entry(0x00100000_00000000L, 0x1.0p+52),
306+
307+
entry(0x00200000_00000000L, 0x1.0p+53),
308+
entry(0x00200000_00000001L, 0x1.0p+53),
309+
entry(0x00200000_00000002L, 0x1.0000000000001p+53),
310+
entry(0x00200000_00000003L, 0x1.0000000000002p+53),
311+
312+
entry(0x7fffffff_fffffa00L, 0x1.ffffffffffffep+62),
313+
entry(0x7fffffff_fffffa01L, 0x1.fffffffffffffp+62),
314+
entry(0x7fffffff_fffffdffL, 0x1.fffffffffffffp+62),
315+
entry(0x7fffffff_fffffe00L, 0x1.0p+63),
316+
entry(0x7fffffff_ffffffffL, 0x1.0p+63), // Long.MAX_VALUE
317+
entry(0x80000000_00000000L, 0x1.0p+63), // Long.MIN_VALUE
318+
entry(0x80000000_00000400L, 0x1.0p+63),
319+
entry(0x80000000_00000401L, 0x1.0000000000001p+63),
320+
entry(0x80000000_00000402L, 0x1.0000000000001p+63),
321+
entry(0x80000000_00000800L, 0x1.0000000000001p+63),
322+
entry(0x80000000_00000bffL, 0x1.0000000000001p+63),
323+
entry(0x80000000_00000c00L, 0x1.0000000000002p+63),
324+
entry(0x80000000_00001000L, 0x1.0000000000002p+63),
325+
entry(0xffffffff_fffff400L, 0x1.ffffffffffffep+63),
326+
entry(0xffffffff_fffff401L, 0x1.fffffffffffffp+63),
327+
entry(0xffffffff_fffff402L, 0x1.fffffffffffffp+63),
328+
entry(0xffffffff_fffffbffL, 0x1.fffffffffffffp+63),
329+
entry(0xffffffff_fffffc00L, 0x1.0p+64),
330+
entry(0xffffffff_ffffffffL, 0x1.0p+64))) {
331+
final long x = pair.getKey();
332+
double expected = pair.getValue();
333+
double actual = ExactMath.unsignedToDouble(x);
334+
Assert.assertEquals("unsignedLongToDouble(%s, 0x%s) = expected: %s, actual: %s".formatted(
335+
Long.toUnsignedString(x), Long.toUnsignedString(x, 16),
336+
Double.toHexString(expected), Double.toHexString(actual)),
337+
expected, actual, 0.0);
338+
}
339+
}
340+
}

0 commit comments

Comments
 (0)