From 454f0086bc9c93127d2851d795756896d12b1630 Mon Sep 17 00:00:00 2001 From: Alexandr Gorshenin Date: Tue, 24 Dec 2024 09:20:40 +0000 Subject: [PATCH 1/4] Added check for +Inf/-Inf for every decimal type --- .../tech/ydb/table/values/DecimalType.java | 51 ++++++++++++++++++- .../tech/ydb/table/values/DecimalValue.java | 40 ++++++++------- 2 files changed, 71 insertions(+), 20 deletions(-) diff --git a/table/src/main/java/tech/ydb/table/values/DecimalType.java b/table/src/main/java/tech/ydb/table/values/DecimalType.java index 968210579..ba132962d 100644 --- a/table/src/main/java/tech/ydb/table/values/DecimalType.java +++ b/table/src/main/java/tech/ydb/table/values/DecimalType.java @@ -16,14 +16,41 @@ public class DecimalType implements Type { public static final int MAX_PRECISION = 35; - private static final DecimalType YDB_DEFAULT = DecimalType.of(22, 9); + private static final InfValues[] INF_VALUES; + + private static final DecimalType YDB_DEFAULT; + + static { + INF_VALUES = new InfValues[DecimalType.MAX_PRECISION]; + + long mask32 = 0xFFFFFFFFL; + long infHigh = 0; + long infLow = 10; + + for (int precision = 1; precision <= DecimalType.MAX_PRECISION; precision++) { + INF_VALUES[precision - 1] = new InfValues(infHigh, infLow); + + // multiply by 10 + long ll = 10 * (infLow & mask32); + long lh = 10 * (infLow >>> 32) + (ll >>> 32); + long hl = 10 * (infHigh & mask32) + (lh >>> 32); + long hh = 10 * (infHigh >>> 32) + (hl >>> 32); + + infLow = (lh << 32) + (ll & mask32); + infHigh = (hh << 32) + (hl & mask32); + } + + YDB_DEFAULT = DecimalType.of(22, 9); + } private final int precision; private final int scale; + private final InfValues inf; private DecimalType(int precision, int scale) { this.precision = precision; this.scale = scale; + this.inf = INF_VALUES[precision - 1]; } public static DecimalType getDefault() { @@ -115,4 +142,26 @@ public DecimalValue newValueUnscaled(BigInteger value) { public DecimalValue newValue(String value) { return DecimalValue.fromString(this, value); } + + boolean isInf(long high, long low) { + return high > inf.posHigh || (high == inf.posHigh && Long.compareUnsigned(low, inf.posLow) >= 0); + } + + boolean isNegInf(long high, long low) { + return high < inf.negHigh || (high == inf.negHigh && Long.compareUnsigned(low, inf.negLow) <= 0); + } + + private static class InfValues { + private final long posHigh; + private final long posLow; + private final long negHigh; + private final long negLow; + + InfValues(long infHigh, long infLow) { + this.posHigh = infHigh; + this.posLow = infLow; + this.negHigh = 0xFFFFFFFFFFFFFFFFL - infHigh; + this.negLow = 0xFFFFFFFFFFFFFFFFL - infLow + 1; + } + } } diff --git a/table/src/main/java/tech/ydb/table/values/DecimalValue.java b/table/src/main/java/tech/ydb/table/values/DecimalValue.java index 3f528bab3..3aaeb584f 100644 --- a/table/src/main/java/tech/ydb/table/values/DecimalValue.java +++ b/table/src/main/java/tech/ydb/table/values/DecimalValue.java @@ -272,18 +272,6 @@ private static long getLongBe(byte[] buf, int from, int to) { return r; } - private static boolean isNan(long high, long low) { - return NAN.high == high && NAN.low == low; - } - - private static boolean isInf(long high, long low) { - return high > INF.high || (high == INF.high && Long.compareUnsigned(low, INF.low) >= 0); - } - - private static boolean isNegInf(long high, long low) { - return high < NEG_INF.high || (high == NEG_INF.high && Long.compareUnsigned(low, NEG_INF.low) <= 0); - } - private static DecimalValue newNan(DecimalType type) { return new DecimalValue(type, NAN.high, NAN.low); } @@ -295,12 +283,27 @@ private static DecimalValue newInf(DecimalType type) { private static DecimalValue newNegInf(DecimalType type) { return new DecimalValue(type, NEG_INF.high, NEG_INF.low); } - static DecimalValue fromUnscaledLong(DecimalType type, long value) { - if (value == 0) { + + private static boolean isNan(long high, long low) { + return NAN.high == high && NAN.low == low; + } + + static DecimalValue fromUnscaledLong(DecimalType type, long low) { + if (low == 0) { return new DecimalValue(type, 0, 0); } - long high = value > 0 ? 0 : -1; - return new DecimalValue(type, high, value); + + long high = low > 0 ? 0 : -1; + + if (type.isInf(high, low)) { + return newInf(type); + } + + if (type.isNegInf(high, low)) { + return newNegInf(type); + } + + return new DecimalValue(type, high, low); } static DecimalValue fromBits(DecimalType type, long high, long low) { @@ -312,11 +315,11 @@ static DecimalValue fromBits(DecimalType type, long high, long low) { return newNan(type); } - if (isInf(high, low)) { + if (type.isInf(high, low)) { return newInf(type); } - if (isNegInf(high, low)) { + if (type.isNegInf(high, low)) { return newNegInf(type); } @@ -515,5 +518,4 @@ static DecimalValue fromBigDecimal(DecimalType type, BigDecimal value) { return DecimalValue.fromUnscaledBigInteger(type, rawValue); } - } From cca97f3d47a4fb2d417613951955f6ace35a3464 Mon Sep 17 00:00:00 2001 From: Alexandr Gorshenin Date: Tue, 24 Dec 2024 16:31:10 +0000 Subject: [PATCH 2/4] Update DecimalType implementation, added +inf/-inf to every instance --- .../tech/ydb/table/values/DecimalType.java | 28 ++++++++-- .../tech/ydb/table/values/DecimalValue.java | 55 ++++++++++--------- 2 files changed, 54 insertions(+), 29 deletions(-) diff --git a/table/src/main/java/tech/ydb/table/values/DecimalType.java b/table/src/main/java/tech/ydb/table/values/DecimalType.java index ba132962d..9d193e36e 100644 --- a/table/src/main/java/tech/ydb/table/values/DecimalType.java +++ b/table/src/main/java/tech/ydb/table/values/DecimalType.java @@ -17,19 +17,17 @@ public class DecimalType implements Type { public static final int MAX_PRECISION = 35; private static final InfValues[] INF_VALUES; - private static final DecimalType YDB_DEFAULT; static { + // Precalculate +inf/-inf values for all precisions INF_VALUES = new InfValues[DecimalType.MAX_PRECISION]; long mask32 = 0xFFFFFFFFL; long infHigh = 0; - long infLow = 10; + long infLow = 1; for (int precision = 1; precision <= DecimalType.MAX_PRECISION; precision++) { - INF_VALUES[precision - 1] = new InfValues(infHigh, infLow); - // multiply by 10 long ll = 10 * (infLow & mask32); long lh = 10 * (infLow >>> 32) + (ll >>> 32); @@ -38,6 +36,8 @@ public class DecimalType implements Type { infLow = (lh << 32) + (ll & mask32); infHigh = (hh << 32) + (hl & mask32); + + INF_VALUES[precision - 1] = new InfValues(infHigh, infLow); } YDB_DEFAULT = DecimalType.of(22, 9); @@ -47,10 +47,18 @@ public class DecimalType implements Type { private final int scale; private final InfValues inf; + private final DecimalValue infValue; + private final DecimalValue negInfValue; + private final DecimalValue nanValue; + private DecimalType(int precision, int scale) { this.precision = precision; this.scale = scale; this.inf = INF_VALUES[precision - 1]; + + this.infValue = new DecimalValue(this, DecimalValue.INF_HIGH, DecimalValue.INF_LOW); + this.negInfValue = new DecimalValue(this, DecimalValue.NEG_INF_HIGH, DecimalValue.NEG_INF_LOW); + this.nanValue = new DecimalValue(this, DecimalValue.NAN_HIGH, DecimalValue.NAN_LOW); } public static DecimalType getDefault() { @@ -82,6 +90,18 @@ public int getScale() { return scale; } + public DecimalValue getInf() { + return infValue; + } + + public DecimalValue getNegInf() { + return negInfValue; + } + + public DecimalValue getNaN() { + return nanValue; + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/table/src/main/java/tech/ydb/table/values/DecimalValue.java b/table/src/main/java/tech/ydb/table/values/DecimalValue.java index 3aaeb584f..2f157b121 100644 --- a/table/src/main/java/tech/ydb/table/values/DecimalValue.java +++ b/table/src/main/java/tech/ydb/table/values/DecimalValue.java @@ -18,21 +18,35 @@ public class DecimalValue implements Value { private static final BigInteger BIGINT_TWO = BigInteger.valueOf(2); + static final long INF_HIGH = 0x0013426172C74D82L; + static final long INF_LOW = 0x2B878FE800000000L; + static final long NEG_INF_HIGH = 0xFFECBD9E8D38B27DL; + static final long NEG_INF_LOW = 0xD478701800000000L; + static final long NAN_HIGH = 0x0013426172C74D82L; + static final long NAN_LOW = 0x2B878FE800000001L; + + /** + * @deprecated * Positive infinity 10^{@value DecimalType#MAX_PRECISION}. */ + @Deprecated public static final DecimalValue INF = new DecimalValue( MAX_DECIMAL, 0x0013426172C74D82L, 0x2B878FE800000000L); /** - * Negative infinity -10^{@value DecimalType#MAX_PRECISION}. + * @deprecated + * Negative infinity -10^{@value DecimalType#MAX_PRECISI0ON}. */ + @Deprecated public static final DecimalValue NEG_INF = new DecimalValue( MAX_DECIMAL, 0xFFECBD9E8D38B27DL, 0xD478701800000000L); /** + * @deprecated * Not a number 10^{@value DecimalType#MAX_PRECISION} + 1. */ + @Deprecated public static final DecimalValue NAN = new DecimalValue( MAX_DECIMAL, 0x0013426172C74D82L, 0x2B878FE800000001L); @@ -60,15 +74,15 @@ public long getLow() { } public boolean isInf() { - return this.high == INF.high && this.low == INF.low; + return this.high == INF_HIGH && this.low == INF_LOW; } public boolean isNegativeInf() { - return this.high == NEG_INF.high && this.low == NEG_INF.low; + return this.high == NEG_INF_HIGH && this.low == NEG_INF_LOW; } public boolean isNan() { - return this.high == NAN.high && this.low == NAN.low; + return this.high == NAN_HIGH && this.low == NAN_LOW; } public boolean isZero() { @@ -123,6 +137,9 @@ public BigDecimal toBigDecimal() { if (isZero()) { return BigDecimal.ZERO.setScale(type.getScale()); } + if (isInf() || isNegativeInf() || isNan()) { + return new BigDecimal(toUnscaledBigInteger()).setScale(type.getScale()); + } return new BigDecimal(toUnscaledBigInteger(), type.getScale()); } @@ -272,18 +289,6 @@ private static long getLongBe(byte[] buf, int from, int to) { return r; } - private static DecimalValue newNan(DecimalType type) { - return new DecimalValue(type, NAN.high, NAN.low); - } - - private static DecimalValue newInf(DecimalType type) { - return new DecimalValue(type, INF.high, INF.low); - } - - private static DecimalValue newNegInf(DecimalType type) { - return new DecimalValue(type, NEG_INF.high, NEG_INF.low); - } - private static boolean isNan(long high, long low) { return NAN.high == high && NAN.low == low; } @@ -296,11 +301,11 @@ static DecimalValue fromUnscaledLong(DecimalType type, long low) { long high = low > 0 ? 0 : -1; if (type.isInf(high, low)) { - return newInf(type); + return type.getInf(); } if (type.isNegInf(high, low)) { - return newNegInf(type); + return type.getNegInf(); } return new DecimalValue(type, high, low); @@ -312,15 +317,15 @@ static DecimalValue fromBits(DecimalType type, long high, long low) { } if (isNan(high, low)) { - return newNan(type); + return type.getNaN(); } if (type.isInf(high, low)) { - return newInf(type); + return type.getInf(); } if (type.isNegInf(high, low)) { - return newNegInf(type); + return type.getNegInf(); } return new DecimalValue(type, high, low); @@ -334,7 +339,7 @@ static DecimalValue fromUnscaledBigInteger(DecimalType type, BigInteger value) { boolean negative = value.signum() < 0; if (bitLength > 128) { - return negative ? newNegInf(type) : newInf(type); + return negative ? type.getNegInf() : type.getInf(); } byte[] buf = value.abs().toByteArray(); @@ -370,7 +375,7 @@ private static DecimalValue fromUnsignedLong(DecimalType type, boolean positive, lowHi = lowHi & HALF_LONG_MASK; if ((high & LONG_SIGN_BIT) != 0) { // number is too big, return infinite - return positive ? newInf(type) : newNegInf(type); + return positive ? type.getInf() : type.getNegInf(); } } @@ -420,11 +425,11 @@ static DecimalValue fromString(DecimalType type, String value) { char c3 = value.charAt(cursor + 2); if ((c1 == 'i' || c1 == 'I') && (c2 == 'n' || c2 == 'N') || (c3 == 'f' || c3 == 'F')) { - return negative ? newNegInf(type) : newInf(type); + return negative ? type.getNegInf() : type.getInf(); } if ((c1 == 'n' || c1 == 'N') && (c2 == 'a' || c2 == 'A') || (c3 == 'n' || c3 == 'N')) { - return new DecimalValue(type, NAN.high, NAN.low); + return type.getNaN(); } } From 2e527433f8d9342b8143adf19300603171f961a5 Mon Sep 17 00:00:00 2001 From: Alexandr Gorshenin Date: Tue, 24 Dec 2024 16:31:34 +0000 Subject: [PATCH 3/4] Update tests of DecimalType/DecimalValue --- .../ydb/table/integration/ValuesReadTest.java | 66 ++++++++ .../ydb/table/values/DecimalValueTest.java | 143 +++++++++++++----- 2 files changed, 174 insertions(+), 35 deletions(-) diff --git a/table/src/test/java/tech/ydb/table/integration/ValuesReadTest.java b/table/src/test/java/tech/ydb/table/integration/ValuesReadTest.java index a5e8400bb..4141275f8 100644 --- a/table/src/test/java/tech/ydb/table/integration/ValuesReadTest.java +++ b/table/src/test/java/tech/ydb/table/integration/ValuesReadTest.java @@ -17,6 +17,8 @@ import tech.ydb.table.result.ValueReader; import tech.ydb.table.rpc.grpc.GrpcTableRpc; import tech.ydb.table.transaction.TxControl; +import tech.ydb.table.values.DecimalType; +import tech.ydb.table.values.DecimalValue; import tech.ydb.table.values.NullType; import tech.ydb.table.values.NullValue; import tech.ydb.table.values.PrimitiveType; @@ -157,4 +159,68 @@ public void timestampReadTest() { Assert.assertEquals("Invalid value \"2106-01-01T00:00:00.000000Z\" for type Timestamp", issues[1].getMessage()); } + + @Test + public void decimalReadTest() { + DataQueryResult result = CTX.supplyResult( + s -> s.executeDataQuery("SELECT " + + "Decimal('9', 1, 0) AS d1, " + + "Decimal('-9', 1, 0) AS d2, " + + "Decimal('99999999999999999999999999999999999', 35, 0) AS d3, " + + "Decimal('-99999999999999999999999999999999999', 35, 0) AS d4, " + + "Decimal('9999999999999999999999999.9999999999', 35, 10) AS d5, " + + "Decimal('-9999999999999999999999999.9999999999', 35, 10) AS d6, " + + "Decimal('9.6', 1, 0) AS d7, " + + "Decimal('-9.6', 1, 0) AS d8, " + + "Decimal('99999999999999999999999999999999999.6', 35, 0) AS d9, " + + "Decimal('-99999999999999999999999999999999999.6', 35, 0) AS d10, " + + "Decimal('9999999999999999999999999.99999999996', 35, 10) AS d11, " + + "Decimal('-9999999999999999999999999.99999999996', 35, 10) AS d12;", + TxControl.serializableRw() + ) + ).join().getValue(); + + Assert.assertEquals(1, result.getResultSetCount()); + + ResultSetReader rs = result.getResultSet(0); + Assert.assertTrue(rs.next()); + + DecimalValue d1 = rs.getColumn("d1").getDecimal(); + DecimalValue d2 = rs.getColumn("d2").getDecimal(); + DecimalValue d3 = rs.getColumn("d3").getDecimal(); + DecimalValue d4 = rs.getColumn("d4").getDecimal(); + DecimalValue d5 = rs.getColumn("d5").getDecimal(); + DecimalValue d6 = rs.getColumn("d6").getDecimal(); + DecimalValue d7 = rs.getColumn("d7").getDecimal(); + DecimalValue d8 = rs.getColumn("d8").getDecimal(); + DecimalValue d9 = rs.getColumn("d9").getDecimal(); + DecimalValue d10 = rs.getColumn("d10").getDecimal(); + DecimalValue d11 = rs.getColumn("d11").getDecimal(); + DecimalValue d12 = rs.getColumn("d12").getDecimal(); + + Assert.assertEquals(DecimalType.of(1).newValue(9), d1); + Assert.assertEquals(DecimalType.of(1).newValue(-9), d2); + Assert.assertEquals(DecimalType.of(35).newValue("99999999999999999999999999999999999"), d3); + Assert.assertEquals(DecimalType.of(35).newValue("-99999999999999999999999999999999999"), d4); + Assert.assertEquals(DecimalType.of(35, 10).newValue("9999999999999999999999999.9999999999"), d5); + Assert.assertEquals(DecimalType.of(35, 10).newValue("-9999999999999999999999999.9999999999"), d6); + + Assert.assertEquals(DecimalType.of(1).getInf(), d7); + Assert.assertEquals(DecimalType.of(1).getNegInf(), d8); + Assert.assertEquals(DecimalType.of(35).getInf(), d9); + Assert.assertEquals(DecimalType.of(35).getNegInf(), d10); + Assert.assertEquals(DecimalType.of(35, 10).getInf(), d11); + Assert.assertEquals(DecimalType.of(35, 10).getNegInf(), d12); + + // All infinity values have the same high & low parts + Assert.assertEquals(d7.getHigh(), d9.getHigh()); + Assert.assertEquals(d7.getHigh(), d11.getHigh()); + Assert.assertEquals(d7.getLow(), d9.getLow()); + Assert.assertEquals(d7.getLow(), d11.getLow()); + + Assert.assertEquals(d8.getHigh(), d10.getHigh()); + Assert.assertEquals(d8.getHigh(), d12.getHigh()); + Assert.assertEquals(d8.getLow(), d10.getLow()); + Assert.assertEquals(d8.getLow(), d12.getLow()); + } } diff --git a/table/src/test/java/tech/ydb/table/values/DecimalValueTest.java b/table/src/test/java/tech/ydb/table/values/DecimalValueTest.java index 0768d790b..a8debfce4 100644 --- a/table/src/test/java/tech/ydb/table/values/DecimalValueTest.java +++ b/table/src/test/java/tech/ydb/table/values/DecimalValueTest.java @@ -67,35 +67,35 @@ public void bigDecimalConv() { @Test public void contract() { - DecimalType type = DecimalType.of(13, 2); + DecimalType type = DecimalType.of(20, 2); DecimalValue value = DecimalValue.fromBits(type, 0x0001, 0x0002); - Assert.assertEquals(DecimalType.of(13, 2), value.getType()); + Assert.assertEquals(DecimalType.of(20, 2), value.getType()); Assert.assertEquals(0x0001, value.getHigh()); Assert.assertEquals(0x0002, value.getLow()); // equals Assert.assertEquals(value, DecimalValue.fromBits(type, 0x0001, 0x0002)); - Assert.assertEquals(value, DecimalValue.fromBits(DecimalType.of(13, 2), 0x0001, 0x0002)); + Assert.assertEquals(value, DecimalValue.fromBits(DecimalType.of(20, 2), 0x0001, 0x0002)); Assert.assertNotEquals(value, DecimalValue.fromBits(type, 0x0001, 0x0003)); Assert.assertNotEquals(value, DecimalValue.fromBits(type, 0x0002, 0x0002)); - Assert.assertNotEquals(value, DecimalValue.fromBits(DecimalType.of(12, 2), 0x0001, 0x0002)); - Assert.assertNotEquals(value, DecimalValue.fromBits(DecimalType.of(13, 1), 0x0001, 0x0002)); + Assert.assertNotEquals(value, DecimalValue.fromBits(DecimalType.of(21, 2), 0x0001, 0x0002)); + Assert.assertNotEquals(value, DecimalValue.fromBits(DecimalType.of(20, 1), 0x0001, 0x0002)); // hashCode Assert.assertEquals(value.hashCode(), DecimalValue.fromBits(type, 0x0001, 0x0002).hashCode()); - Assert.assertEquals(value.hashCode(), DecimalValue.fromBits(DecimalType.of(13, 2), 0x0001, 0x0002).hashCode()); + Assert.assertEquals(value.hashCode(), DecimalValue.fromBits(DecimalType.of(20, 2), 0x0001, 0x0002).hashCode()); Assert.assertNotEquals(value.hashCode(), DecimalValue.fromBits(type, 0x0001, 0x0003).hashCode()); Assert.assertNotEquals(value.hashCode(), DecimalValue.fromBits(type, 0x0002, 0x0002).hashCode()); - Assert.assertNotEquals(value.hashCode(), DecimalValue.fromBits(DecimalType.of(12, 2), 0x0001, 0x0002).hashCode()); - Assert.assertNotEquals(value.hashCode(), DecimalValue.fromBits(DecimalType.of(13, 1), 0x0001, 0x0002).hashCode()); + Assert.assertNotEquals(value.hashCode(), DecimalValue.fromBits(DecimalType.of(21, 2), 0x0001, 0x0002).hashCode()); + Assert.assertNotEquals(value.hashCode(), DecimalValue.fromBits(DecimalType.of(20, 1), 0x0001, 0x0002).hashCode()); } @Test public void protobuf() { - DecimalType type = DecimalType.of(13, 2); + DecimalType type = DecimalType.of(20, 2); DecimalValue value = DecimalValue.fromBits(type, 0x0001, 0x0002); ValueProtos.Value valuePb = value.toPb(); @@ -128,11 +128,83 @@ public void inf() { DecimalValue value = type.newValue(inf); Assert.assertTrue(value.isInf()); Assert.assertFalse(value.isNegative()); - Assert.assertEquals(DecimalValue.INF, value); + Assert.assertEquals(type.getInf(), value); inf = inf.add(k); } } + private void assertIsValid(DecimalValue v) { + Assert.assertFalse("Non expected Nan for " + v, v.isNan()); + Assert.assertFalse("Non expected Inf for " + v, v.isInf()); + Assert.assertFalse("Non expected -Inf for " + v, v.isNegativeInf()); + } + + private void assertIsNan(DecimalValue v) { + Assert.assertTrue("Expected Nan for " + v, v.isNan()); + Assert.assertFalse("Non expected Inf for " + v, v.isInf()); + Assert.assertFalse("Non expected -Inf for " + v, v.isNegativeInf()); + + Assert.assertEquals(DecimalValue.NAN_LOW, v.getLow()); + Assert.assertEquals(DecimalValue.NAN_HIGH, v.getHigh()); + } + + private void assertIsInf(DecimalValue v) { + Assert.assertFalse("Non expected Nan for " + v, v.isNan()); + Assert.assertTrue("Expected Inf for " + v, v.isInf()); + Assert.assertFalse("Non expected -Inf for " + v, v.isNegativeInf()); + + Assert.assertEquals(DecimalValue.INF_LOW, v.getLow()); + Assert.assertEquals(DecimalValue.INF_HIGH, v.getHigh()); + } + + private void assertIsNegInf(DecimalValue v) { + Assert.assertFalse("Non expected Nan for " + v, v.isNan()); + Assert.assertFalse("Non expected Inf for " + v, v.isInf()); + Assert.assertTrue("Expected -Inf for " + v, v.isNegativeInf()); + + Assert.assertEquals(DecimalValue.NEG_INF_LOW, v.getLow()); + Assert.assertEquals(DecimalValue.NEG_INF_HIGH, v.getHigh()); + } + + @Test + public void allTypeInfiniteAndNan() { + BigInteger inf = BigInteger.ONE; + BigInteger nan = new BigInteger("100000000000000000000000000000000001"); + int[] scales = new int[] { 1, 9, 35 }; + for (int precision = 1; precision <= DecimalType.MAX_PRECISION; precision++) { + inf = inf.multiply(BigInteger.TEN); + + DecimalType type = DecimalType.of(precision); + + assertIsInf(type.newValue(inf)); + assertIsNegInf(type.newValue(inf.negate())); + assertIsNan(type.newValue(nan)); + + assertIsValid(type.newValue(inf.subtract(BigInteger.ONE))); + assertIsValid(type.newValue(inf.negate().add(BigInteger.ONE))); + + for (int scale : scales) { + if (scale > precision) { + continue; + } + + DecimalType scaled = DecimalType.of(precision, scale); + BigDecimal scaledInf = new BigDecimal(inf, scale); + BigDecimal scaledNan = new BigDecimal(nan, scale); + + System.out.println("Nan for " + scaled + " -> " + scaledNan); + + assertIsInf(scaled.newValue(scaledInf)); + assertIsNegInf(scaled.newValue(scaledInf.negate())); + + assertIsValid(scaled.newValue(scaledInf.subtract(BigDecimal.valueOf(1, scale)))); + assertIsValid(scaled.newValue(scaledInf.negate().add(BigDecimal.valueOf(1, scale)))); + + assertIsNan(scaled.newValue(scaledNan)); + } + } + } + @Test public void infDefaulttype() { DecimalType type = DecimalType.getDefault(); @@ -143,7 +215,7 @@ public void infDefaulttype() { DecimalValue value = type.newValue(inf); Assert.assertTrue(value.isInf()); Assert.assertFalse(value.isNegative()); - Assert.assertNotEquals(DecimalValue.INF, value); + Assert.assertEquals(type.getInf(), value); inf = inf.add(k); } } @@ -158,7 +230,7 @@ public void negativeInf() { DecimalValue value = type.newValue(inf); Assert.assertTrue(value.isNegativeInf()); Assert.assertTrue(value.isNegative()); - Assert.assertEquals(DecimalValue.NEG_INF, value); + Assert.assertEquals(type.getNegInf(), value); inf = inf.subtract(k); } } @@ -173,7 +245,7 @@ public void negativeInfDefaultType() { DecimalValue value = type.newValue(inf); Assert.assertTrue(value.isNegativeInf()); Assert.assertTrue(value.isNegative()); - Assert.assertNotEquals(DecimalValue.NEG_INF, value); + Assert.assertEquals(type.getNegInf(), value); inf = inf.subtract(k); } } @@ -258,7 +330,7 @@ public void ofString() { @Test public void ofUnsigned() { - DecimalType t = DecimalType.getDefault(); + DecimalType t = DecimalType.of(31, 9); String zeros = "." + String.join("", Collections.nCopies(t.getScale(), "0")); Assert.assertTrue(t.newValueUnsigned(0).isZero()); @@ -278,7 +350,7 @@ public void ofUnsigned() { @Test public void ofLong() { - DecimalType t = DecimalType.getDefault(); + DecimalType t = DecimalType.of(31, 9); String zeros = "." + String.join("", Collections.nCopies(t.getScale(), "0")); Assert.assertTrue(t.newValue(0).isZero()); @@ -575,7 +647,7 @@ public void toUnscaledBigInteger() { // (2) positive numbers: 1, 12, 123, ... String s = ""; - for (int i = 1; i < DecimalType.MAX_PRECISION; i++) { + for (int i = 1; i < t.getPrecision(); i++) { s += Integer.toString(i % 10); BigInteger value = new BigInteger(s); Assert.assertEquals(value, t.newValueUnscaled(value).toUnscaledBigInteger()); @@ -583,7 +655,7 @@ public void toUnscaledBigInteger() { // (3) negative numbers: -1, -12, -123, ... s = "-"; - for (int i = 1; i < DecimalType.MAX_PRECISION; i++) { + for (int i = 1; i < t.getPrecision(); i++) { s += Integer.toString(i % 10); BigInteger value = new BigInteger(s); Assert.assertEquals(value, t.newValueUnscaled(value).toUnscaledBigInteger()); @@ -591,9 +663,9 @@ public void toUnscaledBigInteger() { // (4) -inf, +inf, nan BigInteger inf = BigInteger.TEN.pow(DecimalType.MAX_PRECISION); - Assert.assertEquals(DecimalValue.INF.toUnscaledBigInteger(), inf); - Assert.assertEquals(DecimalValue.NEG_INF.toUnscaledBigInteger(), inf.negate()); - Assert.assertEquals(DecimalValue.NAN.toUnscaledBigInteger(), inf.add(BigInteger.ONE)); + Assert.assertEquals(t.getInf().toUnscaledBigInteger(), inf); + Assert.assertEquals(t.getNegInf().toUnscaledBigInteger(), inf.negate()); + Assert.assertEquals(t.getNaN().toUnscaledBigInteger(), inf.add(BigInteger.ONE)); } @Test @@ -627,18 +699,19 @@ public void toBigInteger() { // (4) -inf, +inf, nan BigInteger inf = BigInteger.TEN.pow(DecimalType.MAX_PRECISION); - Assert.assertEquals(DecimalValue.INF.toBigInteger(), inf); - Assert.assertEquals(DecimalValue.NEG_INF.toBigInteger(), inf.negate()); - Assert.assertEquals(DecimalValue.NAN.toBigInteger(), inf.add(BigInteger.ONE)); + Assert.assertEquals(t.getInf().toBigInteger(), inf); + Assert.assertEquals(t.getNegInf().toBigInteger(), inf.negate()); + Assert.assertEquals(t.getNaN().toBigInteger(), inf.add(BigInteger.ONE)); } @Test public void toBigDecimal() { // (1) special values - BigDecimal inf = BigDecimal.TEN.pow(DecimalType.MAX_PRECISION); - Assert.assertEquals(DecimalValue.INF.toBigDecimal(), inf); - Assert.assertEquals(DecimalValue.NEG_INF.toBigDecimal(), inf.negate()); - Assert.assertEquals(DecimalValue.NAN.toBigDecimal(), inf.add(BigDecimal.ONE)); + BigDecimal inf = BigDecimal.ONE.scaleByPowerOfTen(DecimalType.MAX_PRECISION); + DecimalType type = DecimalType.getDefault(); + Assert.assertEquals(type.getInf().toBigDecimal(), inf.setScale(type.getScale())); + Assert.assertEquals(type.getNegInf().toBigDecimal(), inf.negate().setScale(type.getScale())); + Assert.assertEquals(type.getNaN().toBigDecimal(), inf.add(BigDecimal.ONE).setScale(type.getScale())); // (2) positive numbers Assert.assertEquals(newDecimal(1234567890L, 0).toBigDecimal(), BigDecimal.valueOf(1234567890L, 0)); @@ -682,30 +755,30 @@ public void toUnscaledString() { // (2) positive numbers: 1, 12, 123, ... String s = ""; - for (int i = 1; i < DecimalType.MAX_PRECISION; i++) { + for (int i = 1; i < t.getPrecision(); i++) { s += Integer.toString(i % 10); Assert.assertEquals(s, t.newValueUnscaled(new BigInteger(s)).toUnscaledString()); } // (3) negative numbers: -1, -12, -123, ... s = "-"; - for (int i = 1; i < DecimalType.MAX_PRECISION; i++) { + for (int i = 1; i < t.getPrecision(); i++) { s += Integer.toString(i % 10); Assert.assertEquals(s, t.newValueUnscaled(new BigInteger(s)).toUnscaledString()); } // (4) -inf, +inf, nan - Assert.assertEquals("100000000000000000000000000000000000", DecimalValue.INF.toUnscaledString()); // 10^35 - Assert.assertEquals("-100000000000000000000000000000000000", DecimalValue.NEG_INF.toUnscaledString()); // -10^35 - Assert.assertEquals("100000000000000000000000000000000001", DecimalValue.NAN.toUnscaledString()); // 10^35 + 1 + Assert.assertEquals("100000000000000000000000000000000000", t.getInf().toUnscaledString()); // 10^35 + Assert.assertEquals("-100000000000000000000000000000000000", t.getNegInf().toUnscaledString()); // -10^35 + Assert.assertEquals("100000000000000000000000000000000001", t.getNaN().toUnscaledString()); // 10^35 + 1 } @Test public void toStringTest() { // (1) special values - Assert.assertEquals("inf", DecimalValue.INF.toString()); - Assert.assertEquals("-inf", DecimalValue.NEG_INF.toString()); - Assert.assertEquals("nan", DecimalValue.NAN.toString()); + Assert.assertEquals("inf", DecimalType.getDefault().getInf().toString()); + Assert.assertEquals("-inf", DecimalType.getDefault().getNegInf().toString()); + Assert.assertEquals("nan", DecimalType.getDefault().getNaN().toString()); // (2) positive numbers Assert.assertEquals("1234567890", newDecimal(1234567890L, 0).toString()); From 279a6416215a596be7e45f2b10798220ebd46af5 Mon Sep 17 00:00:00 2001 From: Alexandr Gorshenin Date: Tue, 24 Dec 2024 16:38:43 +0000 Subject: [PATCH 4/4] Fixed javadoc --- .../main/java/tech/ydb/table/values/DecimalValue.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/table/src/main/java/tech/ydb/table/values/DecimalValue.java b/table/src/main/java/tech/ydb/table/values/DecimalValue.java index 2f157b121..7df3efa27 100644 --- a/table/src/main/java/tech/ydb/table/values/DecimalValue.java +++ b/table/src/main/java/tech/ydb/table/values/DecimalValue.java @@ -28,7 +28,8 @@ public class DecimalValue implements Value { /** * @deprecated - * Positive infinity 10^{@value DecimalType#MAX_PRECISION}. + * Positive infinity 10^MAX_PRECISION. + * Use {@link DecimalType#getInf() } instead */ @Deprecated public static final DecimalValue INF = new DecimalValue( @@ -36,7 +37,8 @@ public class DecimalValue implements Value { /** * @deprecated - * Negative infinity -10^{@value DecimalType#MAX_PRECISI0ON}. + * Negative infinity -10^MAX_PRECISI0ON. + * Use {@link DecimalType#getNegInf() } instead */ @Deprecated public static final DecimalValue NEG_INF = new DecimalValue( @@ -44,7 +46,8 @@ public class DecimalValue implements Value { /** * @deprecated - * Not a number 10^{@value DecimalType#MAX_PRECISION} + 1. + * Not a number 10^MAX_PRECISION + 1. + * Use {@link DecimalType#getNaN() } instead */ @Deprecated public static final DecimalValue NAN = new DecimalValue(