From 180409babb93d06345d6a6d953d11dcb7f28ed5e Mon Sep 17 00:00:00 2001 From: Alexei Osipov Date: Fri, 14 Nov 2025 22:58:37 +0100 Subject: [PATCH 1/3] Java: Add Decimal64.toUnderlying() method #110 --- .../java/com/epam/deltix/dfp/Decimal64.java | 16 +++++++++++-- .../com/epam/deltix/dfp/Decimal64Test.java | 5 ++++ .../com/epam/deltix/dfp/JavaImplTest.java | 23 +++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/java/dfp/src/main/java/com/epam/deltix/dfp/Decimal64.java b/java/dfp/src/main/java/com/epam/deltix/dfp/Decimal64.java index d272632b..9a96482e 100644 --- a/java/dfp/src/main/java/com/epam/deltix/dfp/Decimal64.java +++ b/java/dfp/src/main/java/com/epam/deltix/dfp/Decimal64.java @@ -117,7 +117,7 @@ public class Decimal64 extends Number implements Comparable { final long value; - Decimal64(final long value) { + Decimal64(@Decimal final long value) { this.value = value; } @@ -129,7 +129,7 @@ public class Decimal64 extends Number implements Comparable { * @param value 64-bit DFP value * @return new {@code Decimal64} instance */ - public static Decimal64 fromUnderlying(final long value) { + public static Decimal64 fromUnderlying(@Decimal final long value) { return Decimal64Utils.NULL == value ? null : new Decimal64(value); } @@ -139,10 +139,21 @@ public static Decimal64 fromUnderlying(final long value) { * @param obj {@code Decimal64} instance, {@code null} can be passed too * @return underlying binary representation as {@code long} */ + @Decimal public static long toUnderlying(final Decimal64 obj) { return null == obj ? Decimal64Utils.NULL : obj.value; } + /** + * Get binary representation as {@code long} (unboxing). + * + * @return underlying binary representation as {@code long} + */ + @Decimal + public long toUnderlying() { + return value; + } + /** * Create {@code Decimal64} instance from fixed point decimal value: (12345, 2) -> 123.45 * @@ -358,6 +369,7 @@ public boolean isNormal() { * @see #equals(Decimal64, Decimal64) * @see #equals(Object) */ + @SuppressWarnings("NumberEquality") public boolean equals(final Decimal64 other) { return this == other || other != null && Decimal64Utils.equals(this.value, other.value); } diff --git a/java/dfp/src/test/java/com/epam/deltix/dfp/Decimal64Test.java b/java/dfp/src/test/java/com/epam/deltix/dfp/Decimal64Test.java index 1849e803..5b6dd80d 100644 --- a/java/dfp/src/test/java/com/epam/deltix/dfp/Decimal64Test.java +++ b/java/dfp/src/test/java/com/epam/deltix/dfp/Decimal64Test.java @@ -12,6 +12,11 @@ public void unbox() { Assert.assertEquals(Decimal64Utils.NULL, Decimal64.toUnderlying(null)); } + @Test + public void unboxInstance() { + Assert.assertEquals(5L, Decimal64.fromUnderlying(5L).toUnderlying()); + } + @Test public void equality() { diff --git a/java/dfp/src/test/java/com/epam/deltix/dfp/JavaImplTest.java b/java/dfp/src/test/java/com/epam/deltix/dfp/JavaImplTest.java index a267b71a..3d2a1e0a 100644 --- a/java/dfp/src/test/java/com/epam/deltix/dfp/JavaImplTest.java +++ b/java/dfp/src/test/java/com/epam/deltix/dfp/JavaImplTest.java @@ -1112,4 +1112,27 @@ public void issue110BoxedEquals() { assertTrue(Decimal64Utils.compareTo(val2, cache.get("val")) == 0); assertTrue(Decimal64Utils.equals(val2, cache.get("val"))); } + + // https://github.com/epam/DFP/issues/110 + @Test + public void issue110BoxedEquals_2() { + Map cache = new HashMap<>(); + Decimal64 val = Decimal64.fromDouble(0.5); + cache.put("val", val); + @Decimal long val2 = Decimal64Utils.fromDouble(0.5); + + // Compare unboxed value with boxed value (the best way for this case) + assertTrue(Decimal64Utils.equals(val2, cache.get("val"))); + + // Compare boxed values + assertTrue(Decimal64.fromUnderlying(val2).equals(cache.get("val"))); + + // Compare binary representations + assertTrue(val2 == Decimal64.toUnderlying(cache.get("val"))); + assertTrue(Decimal64Utils.compareTo(val2, Decimal64.toUnderlying(cache.get("val"))) == 0); + + // Compare binary representations with use of new toUnderlying() + assertTrue(val2 == cache.get("val").toUnderlying()); // May produce NPE, if no value in the cache! + assertTrue(Decimal64Utils.compareTo(val2, cache.get("val").toUnderlying()) == 0); // May produce NPE, if no value in the cache! + } } From a8f93f5a453abd22103069ab72f1fb7e4aa6225d Mon Sep 17 00:00:00 2001 From: Alexei Osipov Date: Mon, 17 Nov 2025 19:12:12 +0100 Subject: [PATCH 2/3] Cleanup --- java/dfp/src/test/java/com/epam/deltix/dfp/JavaImplTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/java/dfp/src/test/java/com/epam/deltix/dfp/JavaImplTest.java b/java/dfp/src/test/java/com/epam/deltix/dfp/JavaImplTest.java index 3d2a1e0a..bedad1f5 100644 --- a/java/dfp/src/test/java/com/epam/deltix/dfp/JavaImplTest.java +++ b/java/dfp/src/test/java/com/epam/deltix/dfp/JavaImplTest.java @@ -1125,7 +1125,9 @@ public void issue110BoxedEquals_2() { assertTrue(Decimal64Utils.equals(val2, cache.get("val"))); // Compare boxed values - assertTrue(Decimal64.fromUnderlying(val2).equals(cache.get("val"))); + Decimal64 value2boxed = Decimal64.fromUnderlying(val2); + assertTrue(value2boxed.equals(cache.get("val"))); + assertTrue(value2boxed.compareTo(cache.get("val")) == 0); // Compare binary representations assertTrue(val2 == Decimal64.toUnderlying(cache.get("val"))); From 0936fa6889d8338f1c0e38e0301cf6ffb269d8ea Mon Sep 17 00:00:00 2001 From: Alexei Osipov Date: Mon, 17 Nov 2025 22:56:39 +0100 Subject: [PATCH 3/3] Cleanup --- .../com/epam/deltix/dfp/Decimal64Utils.java | 19 ++++++++++++++----- .../com/example/demo/DemoApplication.java | 2 ++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/java/dfp/src/main/java/com/epam/deltix/dfp/Decimal64Utils.java b/java/dfp/src/main/java/com/epam/deltix/dfp/Decimal64Utils.java index 16c390b8..d8923044 100644 --- a/java/dfp/src/main/java/com/epam/deltix/dfp/Decimal64Utils.java +++ b/java/dfp/src/main/java/com/epam/deltix/dfp/Decimal64Utils.java @@ -309,11 +309,6 @@ public static long fromUnderlying(@Decimal final long value) { return value; } - @Decimal - public static long toUnderlying(@Decimal final long value) { - return value; - } - /** * Create {@code @Decimal long} value from {@code BigDecimal} binary floating point value. *

Note that not all binary FP values can be exactly represented as decimal FP values. @@ -3128,6 +3123,20 @@ public static int compareToChecked(@Decimal final long a, final Object b) { return compareTo(a, ((Decimal64) b).value); } + /** + * Checks underlying binary value for null-value and returns it; do not use directly. + * + * @param value 64-bit DFP value + * @return same 64-bit DFP value (unless it is DFP null-value) + * @throws NullPointerException if the value is null + */ + @Deprecated + @Decimal + public static long toUnderlyingChecked(@Decimal final long value) { + checkNull(value); + return value; + } + /// endregion /// region Array boxing/unboxing (array conversions from long[] / to long[]) diff --git a/java/vtaTest/src/main/java/com/example/demo/DemoApplication.java b/java/vtaTest/src/main/java/com/example/demo/DemoApplication.java index 3419ca25..42bc6897 100644 --- a/java/vtaTest/src/main/java/com/example/demo/DemoApplication.java +++ b/java/vtaTest/src/main/java/com/example/demo/DemoApplication.java @@ -39,5 +39,7 @@ public static void main(String[] args) { sDecimal.toScientificString() + "(=0x" + Long.toHexString(Decimal64.toUnderlying(sDecimal)) + "L) ) = " + qDecimal.toScientificString() + "(=0x" + Long.toHexString(Decimal64.toUnderlying(qDecimal)) + "L) ) is a double " + q + "(=0x" + Long.toHexString(Double.doubleToRawLongBits(q)) + "L)"); + + System.out.println(Decimal64.fromUnderlying(aDecimal.toUnderlying())); } }