Require scale for integral conversions and RoundingMode for float 02/100102/4
authorRobert Varga <robert.varga@pantheon.tech>
Sun, 13 Mar 2022 13:04:24 +0000 (14:04 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 13 Mar 2022 14:41:11 +0000 (15:41 +0100)
Integral conversions need to check range and take the intended scale,
otherwise we are left guessing as to what we need to do.

Similarly, float/double need to take RoudingMode, so that they can
figure out what to do with excess precision.

JIRA: YANGTOOLS-1405
Change-Id: I2a6eef9c987dcceec8de7e2aa5bbf43062301cf9
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
common/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Decimal64.java
common/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Decimal64Conversion.java [new file with mode: 0644]
common/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Decimal64Test.java
model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/type/NumberUtil.java
model/yang-model-ri/src/test/java/org/opendaylight/yangtools/yang/model/ri/type/NumberUtilTest.java
parser/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TypedefConstraintsTest.java

index 1905d2c1456ba083e94afc113204989946027d82..c91e42f417935de228eca5f743cfbb06747c1c9b 100644 (file)
@@ -14,6 +14,7 @@ import com.google.common.annotations.Beta;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Strings;
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.util.Optional;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
@@ -140,7 +141,7 @@ public class Decimal64 extends Number implements CanonicalValue<Decimal64> {
 
     private static final int MAX_SCALE = 18;
 
-    private static final long[] SCALE = {
+    private static final long[] FACTOR = {
         10,
         100,
         1000,
@@ -161,11 +162,13 @@ public class Decimal64 extends Number implements CanonicalValue<Decimal64> {
         1000000000000000000L
     };
 
+    private static final Decimal64Conversion[] CONVERSION = Decimal64Conversion.values();
     private static final Decimal64[] MIN_VALUE;
     private static final Decimal64[] MAX_VALUE;
 
     static {
-        verify(SCALE.length == MAX_SCALE);
+        verify(CONVERSION.length == MAX_SCALE);
+        verify(FACTOR.length == MAX_SCALE);
 
         MIN_VALUE = new Decimal64[MAX_SCALE];
         MAX_VALUE = new Decimal64[MAX_SCALE];
@@ -175,24 +178,30 @@ public class Decimal64 extends Number implements CanonicalValue<Decimal64> {
         }
     }
 
-    private final byte scaleOffset;
+    private final byte offset;
     private final long value;
 
     @VisibleForTesting
     Decimal64(final int scale, final long intPart, final long fracPart, final boolean negative) {
-        scaleOffset = offsetOf(scale);
+        offset = offsetOf(scale);
 
-        final long bits = intPart * SCALE[scaleOffset] + fracPart;
+        final long bits = intPart * FACTOR[offset] + fracPart;
         value = negative ? -bits : bits;
     }
 
-    private Decimal64(final byte scaleOffset, final long value) {
-        this.scaleOffset = scaleOffset;
+    private Decimal64(final byte offset, final long intPart, final boolean negative) {
+        this.offset = offset;
+        final long bits = intPart * FACTOR[offset];
+        value = negative ? -bits : bits;
+    }
+
+    private Decimal64(final byte offset, final long value) {
+        this.offset = offset;
         this.value = value;
     }
 
     protected Decimal64(final Decimal64 other) {
-        this(other.scaleOffset, other.value);
+        this(other.offset, other.value);
     }
 
     /**
@@ -229,29 +238,58 @@ public class Decimal64 extends Number implements CanonicalValue<Decimal64> {
         return MAX_VALUE[offsetOf(scale)];
     }
 
-    // >>> FIXME: these need to take a scale value and perform a range check. we also need truncating counterparts
-    public static Decimal64 valueOf(final byte byteVal) {
-        return byteVal < 0 ? new Decimal64(1, -byteVal, 0, true) : new Decimal64(1, byteVal, 0, false);
+    // >>> FIXME: these need truncating counterparts
+    public static Decimal64 valueOf(final int scale, final byte byteVal) {
+        final byte offset = offsetOf(scale);
+        final var conv = CONVERSION[offset];
+        if (byteVal < conv.minByte || byteVal > conv.maxByte) {
+            throw new IllegalArgumentException("Value " + byteVal + " is not in range ["
+                + conv.minByte + ".." + conv.maxByte + "] to fit scale " + scale);
+        }
+        return byteVal < 0 ? new Decimal64(offset, -byteVal, true) : new Decimal64(offset, byteVal, false);
+    }
+
+    public static Decimal64 valueOf(final int scale, final short shortVal) {
+        final byte offset = offsetOf(scale);
+        final var conv = CONVERSION[offset];
+        if (shortVal < conv.minShort || shortVal > conv.maxShort) {
+            throw new IllegalArgumentException("Value " + shortVal + " is not in range ["
+                + conv.minShort + ".." + conv.maxShort + "] to fit scale " + scale);
+        }
+        return shortVal < 0 ? new Decimal64(offset, -shortVal, true) : new Decimal64(offset, shortVal, false);
     }
 
-    public static Decimal64 valueOf(final short shortVal) {
-        return shortVal < 0 ? new Decimal64(1, -shortVal, 0, true) : new Decimal64(1, shortVal, 0, false);
+    public static Decimal64 valueOf(final int scale, final int intVal) {
+        final byte offset = offsetOf(scale);
+        final var conv = CONVERSION[offset];
+        if (intVal < conv.minInt || intVal > conv.maxInt) {
+            throw new IllegalArgumentException("Value " + intVal + " is not in range ["
+                + conv.minInt + ".." + conv.maxInt + "] to fit scale " + scale);
+        }
+        return intVal < 0 ? new Decimal64(offset, - (long)intVal, true) : new Decimal64(offset, intVal, false);
     }
 
-    public static Decimal64 valueOf(final int intVal) {
-        return intVal < 0 ? new Decimal64(1, - (long)intVal, 0, true) : new Decimal64(1, intVal, 0, false);
+    public static Decimal64 valueOf(final int scale, final long longVal) {
+        final byte offset = offsetOf(scale);
+        final var conv = CONVERSION[offset];
+        if (longVal < conv.minLong || longVal > conv.maxLong) {
+            throw new IllegalArgumentException("Value " + longVal + " is not in range ["
+                + conv.minLong + ".." + conv.maxLong + "] to fit scale " + scale);
+        }
+        return longVal < 0 ? new Decimal64(offset, -longVal, true) : new Decimal64(offset, longVal, false);
     }
+    // <<< FIXME
 
-    public static Decimal64 valueOf(final long longVal) {
+    // FIXME: this should take a RoundingMode and perform rounding
+    // FIXME: this should have a truncating counterpart
+    public static Decimal64 valueOf(final float floatVal, final RoundingMode rounding) {
         // XXX: we should be able to do something smarter here
-        return valueOf(Long.toString(longVal));
+        return valueOf(Float.toString(floatVal));
     }
-    // <<< FIXME
 
     // FIXME: this should take a RoundingMode and perform rounding
-    // FIXME: this should have a float counterpart
-    // FIXME: this should have a truncating
-    public static Decimal64 valueOf(final double doubleVal) {
+    // FIXME: this should have a truncating counterpart
+    public static Decimal64 valueOf(final double doubleVal, final RoundingMode rounding) {
         // XXX: we should be able to do something smarter here
         return valueOf(Double.toString(doubleVal));
     }
@@ -286,7 +324,7 @@ public class Decimal64 extends Number implements CanonicalValue<Decimal64> {
      * @return This decimal's scale
      */
     public final int scale() {
-        return scaleOffset + 1;
+        return offset + 1;
     }
 
     /**
@@ -319,7 +357,7 @@ public class Decimal64 extends Number implements CanonicalValue<Decimal64> {
 
     @Override
     public final double doubleValue() {
-        return 1.0 * value / SCALE[scaleOffset];
+        return 1.0 * value / FACTOR[offset];
     }
 
     /**
@@ -393,7 +431,7 @@ public class Decimal64 extends Number implements CanonicalValue<Decimal64> {
         if (this == o) {
             return 0;
         }
-        if (scaleOffset == o.scaleOffset) {
+        if (offset == o.offset) {
             return Long.compare(value, o.value);
         }
 
@@ -454,17 +492,17 @@ public class Decimal64 extends Number implements CanonicalValue<Decimal64> {
     }
 
     private boolean equalsImpl(final Decimal64 other) {
-        return scaleOffset == other.scaleOffset ? value == other.value
+        return offset == other.offset ? value == other.value
                 // We need to normalize both
                 : intPart() == other.intPart() && fracPart() == other.fracPart();
     }
 
     private long intPart() {
-        return value / SCALE[scaleOffset];
+        return value / FACTOR[offset];
     }
 
     private long fracPart() {
-        return Math.abs(value % SCALE[scaleOffset]);
+        return Math.abs(value % FACTOR[offset]);
     }
 
     private static byte offsetOf(final int scale) {
diff --git a/common/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Decimal64Conversion.java b/common/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Decimal64Conversion.java
new file mode 100644 (file)
index 0000000..466995a
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Conversion constants for all scales supported by Decimal64.
+ */
+@NonNullByDefault
+enum Decimal64Conversion {
+    SCALE_1(Byte.MIN_VALUE, Byte.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE,
+            -922337203685477580L,   922337203685477580L,
+            -922337203685477580.8F, 922337203685477580.7F,
+            -922337203685477580.8D, 922337203685477580.7D),
+    SCALE_2(Byte.MIN_VALUE, Byte.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE,
+            -92233720368547758L,    92233720368547758L,
+            -92233720368547758.08F, 92233720368547758.07F,
+            -92233720368547758.08F, 92233720368547758.07D),
+    SCALE_3(Byte.MIN_VALUE, Byte.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE,
+            -9223372036854775L,     9223372036854775L,
+            -9223372036854775.808F, 9223372036854775.807F,
+            -9223372036854775.808D, 9223372036854775.807D),
+    SCALE_4(Byte.MIN_VALUE, Byte.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE,
+            -922337203685477L,      922337203685477L,
+            -922337203685477.5808F, 922337203685477.5807F,
+            -922337203685477.5808D, 922337203685477.5807D),
+    SCALE_5(Byte.MIN_VALUE, Byte.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE,
+            -92233720368547L,       92233720368547L,
+            -92233720368547.75808F, 92233720368547.75807F,
+            -92233720368547.75808D, 92233720368547.75807D),
+    SCALE_6(Byte.MIN_VALUE, Byte.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE,
+            -9223372036854L,        9223372036854L,
+            -9223372036854.775808F, 9223372036854.775807F,
+            -9223372036854.775808D, 9223372036854.775807D),
+    SCALE_7(Byte.MIN_VALUE, Byte.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE,
+            -922337203685L,         922337203685L,
+            -922337203685.4775808F, 922337203685.4775807F,
+            -922337203685.4775808D, 922337203685.4775807D),
+    SCALE_8(Byte.MIN_VALUE, Byte.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE,
+            -92233720368L,          92233720368L,
+            -92233720368.54775808F, 92233720368.54775807F,
+            -92233720368.54775808D, 92233720368.54775807D),
+    SCALE_9(Byte.MIN_VALUE, Byte.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE,
+            -9223372036L,           9223372036L,
+            -9223372036.854775808F, 9223372036.854775807F,
+            -9223372036.854775808D, 9223372036.854775807D),
+    SCALE_10(Byte.MIN_VALUE, Byte.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, -922337203, 922337203,
+            -922337203L,            922337203L,
+            -922337203.6854775808F, 922337203.6854775807F,
+            -922337203.6854775808D, 922337203.6854775807D),
+    SCALE_11(Byte.MIN_VALUE, Byte.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, -92233720, 92233720,
+            -92233720L,             92233720L,
+            -92233720.36854775808F, 92233720.36854775807F,
+            -92233720.36854775808D, 92233720.36854775807D),
+    SCALE_12(Byte.MIN_VALUE, Byte.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, -9223372, 9223372, -9223372L, 9223372L,
+            -9223372.036854775808F, 9223372.036854775807F,
+            -9223372.036854775808D, 9223372.036854775807D),
+    SCALE_13(Byte.MIN_VALUE, Byte.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, -922337, 922337, -922337L, 922337L,
+            -922337.2036854775808F, 922337.2036854775807F,
+            -922337.2036854775808D, 922337.2036854775807D),
+    SCALE_14(Byte.MIN_VALUE, Byte.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, -92233, 92233, -92233L, 92233L,
+            -92233.72036854775808F, 92233.72036854775807F,
+            -92233.72036854775808D, 92233.72036854775807D),
+    SCALE_15(Byte.MIN_VALUE, Byte.MAX_VALUE, (short) -9223, (short) 9223, -9223, 9223, -9223L, 9223L,
+            -9223.372036854775808F, 9223.372036854775807F,
+            -9223.372036854775808D, 9223.372036854775807D),
+    SCALE_16(Byte.MIN_VALUE, Byte.MAX_VALUE, (short) -922, (short) 922, -922, 922, -922L, 922L,
+            -922.3372036854775808F, 922.3372036854775807F,
+            -922.3372036854775808D, 922.3372036854775807D),
+    SCALE_17((byte) -92, (byte)92, (short) -92, (short) 92, -92, 92, -92L, 92L,
+            -92.23372036854775808F, 92.23372036854775807F,
+            -92.23372036854775808D, 92.23372036854775807D),
+    SCALE_18((byte) -9, (byte)9, (short) -9, (short) 9, -9, 9, -9L, 9L,
+            -9.223372036854775808F, 9.223372036854775807F,
+            -9.223372036854775808D, 9.223372036854775807D);
+
+    final byte minByte;
+    final byte maxByte;
+
+    final short minShort;
+    final short maxShort;
+
+    final int minInt;
+    final int maxInt;
+
+    final long minLong;
+    final long maxLong;
+
+    final float minFloat;
+    final float maxFloat;
+
+    final double minDouble;
+    final double maxDouble;
+
+    Decimal64Conversion(
+            final byte minByte, final byte maxByte,
+            final short minShort, final short maxShort,
+            final int minInt, final int maxInt,
+            final long minLong, final long maxLong,
+            final float minFloat, final float maxFloat,
+            final double minDouble, final double maxDouble) {
+        this.minByte = minByte;
+        this.maxByte = maxByte;
+        this.minShort = minShort;
+        this.maxShort = maxShort;
+        this.minInt = minInt;
+        this.maxInt = maxInt;
+        this.minLong = minLong;
+        this.maxLong = maxLong;
+        this.minFloat = minFloat;
+        this.maxFloat = maxFloat;
+        this.minDouble = minDouble;
+        this.maxDouble = maxDouble;
+    }
+}
index e2b01871d7909a840348c9a68692a094f84f20c9..8735a930da92b114c03831cee1783831b44f6057 100644 (file)
@@ -13,6 +13,7 @@ import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import org.junit.Test;
 
 public class Decimal64Test {
@@ -192,25 +193,29 @@ public class Decimal64Test {
 
     @Test
     public void testFactories() {
-        assertEquals("0.0", Decimal64.valueOf((byte) 0).toString());
-        assertEquals("1.0", Decimal64.valueOf((byte) 1).toString());
-        assertEquals("-1.0", Decimal64.valueOf((byte) -1).toString());
+        assertEquals("0.0", Decimal64.valueOf(1, (byte) 0).toString());
+        assertEquals("1.0", Decimal64.valueOf(1, (byte) 1).toString());
+        assertEquals("-1.0", Decimal64.valueOf(1, (byte) -1).toString());
 
-        assertEquals("0.0", Decimal64.valueOf((short) 0).toString());
-        assertEquals("1.0", Decimal64.valueOf((short) 1).toString());
-        assertEquals("-1.0", Decimal64.valueOf((short) -1).toString());
+        assertEquals("0.0", Decimal64.valueOf(1, (short) 0).toString());
+        assertEquals("1.0", Decimal64.valueOf(1, (short) 1).toString());
+        assertEquals("-1.0", Decimal64.valueOf(1, (short) -1).toString());
 
-        assertEquals("0.0", Decimal64.valueOf(0).toString());
-        assertEquals("1.0", Decimal64.valueOf(1).toString());
-        assertEquals("-1.0", Decimal64.valueOf(-1).toString());
+        assertEquals("0.0", Decimal64.valueOf(1, 0).toString());
+        assertEquals("1.0", Decimal64.valueOf(1, 1).toString());
+        assertEquals("-1.0", Decimal64.valueOf(1, -1).toString());
 
-        assertEquals("0.0", Decimal64.valueOf(0L).toString());
-        assertEquals("1.0", Decimal64.valueOf(1L).toString());
-        assertEquals("-1.0", Decimal64.valueOf(-1L).toString());
+        assertEquals("0.0", Decimal64.valueOf(1, 0L).toString());
+        assertEquals("1.0", Decimal64.valueOf(1, 1L).toString());
+        assertEquals("-1.0", Decimal64.valueOf(1, -1L).toString());
 
-        assertEquals("0.0", Decimal64.valueOf(0.0).toString());
-        assertEquals("1.0", Decimal64.valueOf(1.0).toString());
-        assertEquals("-1.0", Decimal64.valueOf(-1.0).toString());
+        assertEquals("0.0", Decimal64.valueOf(0.0F, RoundingMode.UNNECESSARY).toString());
+        assertEquals("1.0", Decimal64.valueOf(1.0F, RoundingMode.UNNECESSARY).toString());
+        assertEquals("-1.0", Decimal64.valueOf(-1.0F, RoundingMode.UNNECESSARY).toString());
+
+        assertEquals("0.0", Decimal64.valueOf(0.0D, RoundingMode.UNNECESSARY).toString());
+        assertEquals("1.0", Decimal64.valueOf(1.0D, RoundingMode.UNNECESSARY).toString());
+        assertEquals("-1.0", Decimal64.valueOf(-1.0D, RoundingMode.UNNECESSARY).toString());
 
         assertEquals("0.0", Decimal64.valueOf(BigDecimal.ZERO).toString());
         assertEquals("1.0", Decimal64.valueOf(BigDecimal.ONE).toString());
@@ -219,18 +224,18 @@ public class Decimal64Test {
 
     @Test
     public void testBoundaries() {
-        assertEquals(-128L, Decimal64.valueOf(Byte.MIN_VALUE).longValue());
-        assertEquals(127L, Decimal64.valueOf(Byte.MAX_VALUE).longValue());
-        assertEquals(-32768L, Decimal64.valueOf(Short.MIN_VALUE).longValue());
-        assertEquals(32767L, Decimal64.valueOf(Short.MAX_VALUE).longValue());
-        assertEquals(-2147483648L, Decimal64.valueOf(Integer.MIN_VALUE).longValue());
-        assertEquals(2147483647L, Decimal64.valueOf(Integer.MAX_VALUE).longValue());
+        assertEquals(-128L, Decimal64.valueOf(1, Byte.MIN_VALUE).longValue());
+        assertEquals(127L, Decimal64.valueOf(1, Byte.MAX_VALUE).longValue());
+        assertEquals(-32768L, Decimal64.valueOf(2, Short.MIN_VALUE).longValue());
+        assertEquals(32767L, Decimal64.valueOf(2, Short.MAX_VALUE).longValue());
+        assertEquals(-2147483648L, Decimal64.valueOf(3, Integer.MIN_VALUE).longValue());
+        assertEquals(2147483647L, Decimal64.valueOf(3, Integer.MAX_VALUE).longValue());
     }
 
     @Test
     public void testByteValueExact() {
-        assertEquals(Byte.MIN_VALUE, Decimal64.valueOf(Byte.MIN_VALUE).byteValueExact());
-        assertEquals(Byte.MAX_VALUE, Decimal64.valueOf(Byte.MAX_VALUE).byteValueExact());
+        assertEquals(Byte.MIN_VALUE, Decimal64.valueOf(1, Byte.MIN_VALUE).byteValueExact());
+        assertEquals(Byte.MAX_VALUE, Decimal64.valueOf(1, Byte.MAX_VALUE).byteValueExact());
     }
 
     @Test
@@ -241,14 +246,14 @@ public class Decimal64Test {
 
     @Test
     public void testByteValueExactRange() {
-        final Decimal64 dec = Decimal64.valueOf(Byte.MAX_VALUE + 1);
+        final Decimal64 dec = Decimal64.valueOf(1, Byte.MAX_VALUE + 1);
         assertThrows(ArithmeticException.class, () -> dec.byteValueExact());
     }
 
     @Test
     public void testShortValueExact() {
-        assertEquals(Short.MIN_VALUE, Decimal64.valueOf(Short.MIN_VALUE).shortValueExact());
-        assertEquals(Short.MAX_VALUE, Decimal64.valueOf(Short.MAX_VALUE).shortValueExact());
+        assertEquals(Short.MIN_VALUE, Decimal64.valueOf(1, Short.MIN_VALUE).shortValueExact());
+        assertEquals(Short.MAX_VALUE, Decimal64.valueOf(1, Short.MAX_VALUE).shortValueExact());
     }
 
     @Test
@@ -259,14 +264,14 @@ public class Decimal64Test {
 
     @Test
     public void testShortValueExactRange() {
-        final Decimal64 dec = Decimal64.valueOf(Short.MAX_VALUE + 1);
+        final Decimal64 dec = Decimal64.valueOf(1, Short.MAX_VALUE + 1);
         assertThrows(ArithmeticException.class, () -> dec.shortValueExact());
     }
 
     @Test
     public void testIntValueExact() {
-        assertEquals(Integer.MIN_VALUE, Decimal64.valueOf(Integer.MIN_VALUE).intValueExact());
-        assertEquals(Integer.MAX_VALUE, Decimal64.valueOf(Integer.MAX_VALUE).intValueExact());
+        assertEquals(Integer.MIN_VALUE, Decimal64.valueOf(1, Integer.MIN_VALUE).intValueExact());
+        assertEquals(Integer.MAX_VALUE, Decimal64.valueOf(1, Integer.MAX_VALUE).intValueExact());
     }
 
     @Test
@@ -277,7 +282,7 @@ public class Decimal64Test {
 
     @Test
     public void testIntValueExactRange() {
-        final Decimal64 dec = Decimal64.valueOf(Integer.MAX_VALUE + 1L);
+        final Decimal64 dec = Decimal64.valueOf(1, Integer.MAX_VALUE + 1L);
         assertThrows(ArithmeticException.class, () -> dec.intValueExact());
     }
 
@@ -287,6 +292,68 @@ public class Decimal64Test {
         assertThrows(ArithmeticException.class, () -> dec.longValueExact());
     }
 
+    @Test
+    public void testLongValueOfBits() {
+        final Decimal64 dec = Decimal64.valueOf(2, 25552555555L);
+        assertEquals(2, dec.scale());
+        assertEquals(2555255555500L, dec.unscaledValue());
+    }
+
+    @Test
+    public void testLongValueOfNegativeBits() {
+        final Decimal64 dec = Decimal64.valueOf(2, -25552555555L);
+        assertEquals(2, dec.scale());
+        assertEquals(-2555255555500L, dec.unscaledValue());
+    }
+
+    @Test
+    public void testByteRange() {
+        for (int i = 1; i <= 16; ++i) {
+            assertEquals(Byte.MIN_VALUE, Decimal64.valueOf(i, Byte.MIN_VALUE).byteValueExact());
+            assertEquals(Byte.MAX_VALUE, Decimal64.valueOf(i, Byte.MAX_VALUE).byteValueExact());
+        }
+        for (int i = 17; i <= 18; ++i) {
+            int scale = i;
+            assertThrows(IllegalArgumentException.class, () -> Decimal64.valueOf(scale, Byte.MIN_VALUE));
+            assertThrows(IllegalArgumentException.class, () -> Decimal64.valueOf(scale, Byte.MAX_VALUE));
+        }
+    }
+
+    @Test
+    public void testShortRange() {
+        for (int i = 1; i <= 14; ++i) {
+            assertEquals(Short.MIN_VALUE, Decimal64.valueOf(i, Short.MIN_VALUE).shortValueExact());
+            assertEquals(Short.MAX_VALUE, Decimal64.valueOf(i, Short.MAX_VALUE).shortValueExact());
+        }
+        for (int i = 15; i <= 18; ++i) {
+            int scale = i;
+            assertThrows(IllegalArgumentException.class, () -> Decimal64.valueOf(scale, Short.MIN_VALUE));
+            assertThrows(IllegalArgumentException.class, () -> Decimal64.valueOf(scale, Short.MAX_VALUE));
+        }
+    }
+
+    @Test
+    public void testIntRange() {
+        for (int i = 1; i <= 9; ++i) {
+            assertEquals(Integer.MIN_VALUE, Decimal64.valueOf(i, Integer.MIN_VALUE).intValueExact());
+            assertEquals(Integer.MAX_VALUE, Decimal64.valueOf(i, Integer.MAX_VALUE).intValueExact());
+        }
+        for (int i = 10; i <= 18; ++i) {
+            int scale = i;
+            assertThrows(IllegalArgumentException.class, () -> Decimal64.valueOf(scale, Integer.MIN_VALUE));
+            assertThrows(IllegalArgumentException.class, () -> Decimal64.valueOf(scale, Integer.MAX_VALUE));
+        }
+    }
+
+    @Test
+    public void testLongRange() {
+        for (int i = 1; i <= 18; ++i) {
+            int scale = i;
+            assertThrows(IllegalArgumentException.class, () -> Decimal64.valueOf(scale, Long.MIN_VALUE));
+            assertThrows(IllegalArgumentException.class, () -> Decimal64.valueOf(scale, Long.MAX_VALUE));
+        }
+    }
+
     private static void assertCanonicalVariants(final String str, final long intPart, final long fracPart,
             final int digits) {
         assertCanonicalString(str, intPart, fracPart, digits, false);
index a96815fa0b9245d11ca4662690e50c8710b233d5..e34e7d8e9f7dd3a821ce1db44fad3d96fb6c0101 100644 (file)
@@ -92,7 +92,8 @@ final class NumberUtil {
                 return input;
             }
             if (input instanceof Byte || input instanceof Short || input instanceof Integer || input instanceof Long) {
-                return Decimal64.valueOf(input.longValue());
+                // FIXME: this is not right, as we need to know fraction-digits
+                return Decimal64.valueOf(1, input.longValue());
             }
 
             return Decimal64.valueOf(input.toString());
index 787c8c7c1940ecdeeb6b9831df30116b7feefa06..791f3dc35371846f9306bf279cd051b31241da98 100644 (file)
@@ -44,10 +44,10 @@ public class NumberUtilTest {
 
     @Test
     public void testRangeCoveredForDecimal64() {
-        final Decimal64 min = Decimal64.valueOf(100.0);
-        final Decimal64 superMin = Decimal64.valueOf(50.0);
-        final Decimal64 max = Decimal64.valueOf(200.0);
-        final Decimal64 superMax = Decimal64.valueOf(300.0);
+        final Decimal64 min = Decimal64.valueOf("100.0");
+        final Decimal64 superMin = Decimal64.valueOf("50.0");
+        final Decimal64 max = Decimal64.valueOf("200.0");
+        final Decimal64 superMax = Decimal64.valueOf("300.0");
 
         assertTrue(NumberUtil.isRangeCovered(min, max, superMin, superMax));
     }
@@ -138,7 +138,7 @@ public class NumberUtilTest {
 
     @Test
     public void testConverterToBigDecimal() {
-        Decimal64 bigDecNum = Decimal64.valueOf(20.0);
+        final Decimal64 bigDecNum = Decimal64.valueOf("20.0");
         final Function<Number, Decimal64> numberFunction = NumberUtil.converterTo(Decimal64.class);
         assertEquals(bigDecNum, numberFunction.apply(bigDecNum));
 
@@ -146,7 +146,6 @@ public class NumberUtilTest {
         assertEquals(bigDecNum, numberFunction.apply(intNum));
 
         double doubleNum = 20.0;
-        bigDecNum = Decimal64.valueOf("20.0");
         assertEquals(bigDecNum, numberFunction.apply(doubleNum));
     }
 
index 33406be696722799d715baa958925bd85551ae34..fb5cf74e7adcbfbc8bae3f86d5336938d9f9ed32 100644 (file)
@@ -64,8 +64,8 @@ public class TypedefConstraintsTest {
         assertEquals(1, decRangeConstraints.size());
 
         final Range<?> range = decRangeConstraints.iterator().next();
-        assertEquals(Decimal64.valueOf(1.5), range.lowerEndpoint());
-        assertEquals(Decimal64.valueOf(5.5), range.upperEndpoint());
+        assertEquals(Decimal64.valueOf("1.5"), range.lowerEndpoint());
+        assertEquals(Decimal64.valueOf("5.5"), range.upperEndpoint());
 
         assertEquals(TypeDefinitions.DECIMAL64.bindTo(leafDecimal.getQName().getModule()), decType.getQName());
         assertNull(decType.getBaseType());