| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <cstdint> |
| #include <iostream> |
| #include <limits> |
| #include <sstream> |
| #include <boost/lexical_cast.hpp> |
| #include "runtime/decimal-value.inline.h" |
| #include "runtime/raw-value.h" |
| #include "runtime/types.h" |
| #include "testutil/gtest-util.h" |
| #include "util/decimal-util.h" |
| #include "util/string-parser.h" |
| |
| #include "common/names.h" |
| |
| using std::max; |
| using std::min; |
| |
| namespace impala { |
| |
| // Compare decimal result against double. |
| static const double MAX_ERROR = 0.00005; |
| |
| template <typename T> |
| void VerifyEquals(const DecimalValue<T>& t1, const DecimalValue<T>& t2) { |
| if (t1 != t2) { |
| LOG(ERROR) << t1 << " != " << t2; |
| EXPECT_TRUE(false); |
| } |
| } |
| |
| template <typename T> |
| void VerifyParse(const string& s, int precision, int scale, bool round, |
| const DecimalValue<T>& expected_val, StringParser::ParseResult expected_result) { |
| StringParser::ParseResult parse_result; |
| DecimalValue<T> val = StringParser::StringToDecimal<T>( |
| s.c_str(), s.size(), precision, scale, round, &parse_result); |
| EXPECT_EQ(expected_result, parse_result) << "Failed test string: " << s; |
| if (expected_result == StringParser::PARSE_SUCCESS || |
| expected_result == StringParser::PARSE_UNDERFLOW) { |
| VerifyEquals(expected_val, val); |
| } |
| } |
| |
| template <typename T> |
| void VerifyParse(const string& s, int precision, int scale, |
| const DecimalValue<T>& expected_val, StringParser::ParseResult expected_result) { |
| VerifyParse(s, precision, scale, false, expected_val, expected_result); |
| VerifyParse(s, precision, scale, true, expected_val, expected_result); |
| } |
| |
| template <typename T> |
| void VerifyParse(const string& s, int precision, int scale, |
| const DecimalValue<T>& expected_val_v1, StringParser::ParseResult expected_result_v1, |
| const DecimalValue<T>& expected_val_v2, StringParser::ParseResult expected_result_v2) |
| { |
| VerifyParse(s, precision, scale, false, expected_val_v1, expected_result_v1); |
| VerifyParse(s, precision, scale, true, expected_val_v2, expected_result_v2); |
| } |
| |
| template<typename T> |
| void VerifyToString(const T& decimal, int precision, int scale, const string& expected) { |
| EXPECT_EQ(decimal.ToString(precision, scale), expected); |
| } |
| |
| void StringToAllDecimals(const string& s, int precision, int scale, int32_t val, |
| StringParser::ParseResult result) { |
| VerifyParse(s, precision, scale, Decimal4Value(val), result); |
| VerifyParse(s, precision, scale, Decimal8Value(val), result); |
| VerifyParse(s, precision, scale, Decimal16Value(val), result); |
| } |
| |
| void StringToAllDecimals(const string& s, int precision, int scale, |
| int32_t val_v1, StringParser::ParseResult result_v1, |
| int32_t val_v2, StringParser::ParseResult result_v2) { |
| VerifyParse(s, precision, scale, |
| Decimal4Value(val_v1), result_v1, Decimal4Value(val_v2), result_v2); |
| VerifyParse(s, precision, scale, |
| Decimal8Value(val_v1), result_v1, Decimal8Value(val_v2), result_v2); |
| VerifyParse(s, precision, scale, |
| Decimal16Value(val_v1), result_v1, Decimal16Value(val_v2), result_v2); |
| } |
| |
| TEST(DecimalTest, IntToDecimal) { |
| Decimal16Value d16; |
| bool overflow = false; |
| |
| d16 = Decimal16Value::FromInt(27, 18, -25559, &overflow); |
| EXPECT_FALSE(overflow); |
| VerifyToString(d16, 27, 18, "-25559.000000000000000000"); |
| |
| d16 = Decimal16Value::FromInt(36, 29, 32130, &overflow); |
| EXPECT_FALSE(overflow); |
| VerifyToString(d16, 36, 29, "32130.00000000000000000000000000000"); |
| |
| d16 = Decimal16Value::FromInt(38, 38, 1, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| // Smaller decimal types can't overflow here since the FE should never generate |
| // that. |
| } |
| |
| TEST(DecimalTest, DoubleToDecimal) { |
| Decimal4Value d4; |
| Decimal8Value d8; |
| Decimal16Value d16; |
| |
| bool overflow = false; |
| d4 = Decimal4Value::FromDouble(9, 0, 1.9, true, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d4.value(), 2); |
| VerifyToString(d4, 9, 0, "2"); |
| |
| d4 = Decimal4Value::FromDouble(9, 0, 1.9, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d4.value(), 1); |
| VerifyToString(d4, 9, 0, "1"); |
| |
| d4 = Decimal4Value::FromDouble(1, 0, 1, true, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d4.value(), 1); |
| VerifyToString(d4, 1, 0, "1"); |
| |
| d4 = Decimal4Value::FromDouble(1, 0, 0, true, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d4.value(), 0); |
| VerifyToString(d4, 1, 0, "0"); |
| |
| d4 = Decimal4Value::FromDouble(1, 0, 9.9, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d4.value(), 9); |
| VerifyToString(d4, 1, 0, "9"); |
| |
| d4 = Decimal4Value::FromDouble(1, 0, 9.9, true, &overflow); |
| EXPECT_TRUE(overflow); |
| overflow = false; |
| |
| d4 = Decimal4Value::FromDouble(1, 0, -1, true, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d4.value(), -1); |
| VerifyToString(d4, 1, 0, "-1"); |
| |
| d4 = Decimal4Value::FromDouble(1, 0, -9.9, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d4.value(), -9); |
| VerifyToString(d4, 1, 0, "-9"); |
| |
| d4 = Decimal4Value::FromDouble(1, 0, -9.9, true, &overflow); |
| EXPECT_TRUE(overflow); |
| overflow = false; |
| |
| d4 = Decimal4Value::FromDouble(1, 1, 0.1, true, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d4.value(), 1); |
| VerifyToString(d4, 1, 1, "0.1"); |
| |
| d4 = Decimal4Value::FromDouble(1, 1, 0.0, true, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d4.value(), 0); |
| VerifyToString(d4, 1, 1, "0.0"); |
| |
| d4 = Decimal4Value::FromDouble(1, 1, -0.1, true, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d4.value(), -1); |
| VerifyToString(d4, 1, 1, "-0.1"); |
| |
| overflow = false; |
| d8 = Decimal8Value::FromDouble(10, 5, -100.1, true, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d8.value(), -10010000); |
| VerifyToString(d8, 10, 5, "-100.10000"); |
| |
| overflow = false; |
| d16 = Decimal16Value::FromDouble(10, 10, -0.1, true, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d16.value(), -1000000000); |
| VerifyToString(d16, 10, 10, "-0.1000000000"); |
| |
| // Test overflow |
| overflow = false; |
| Decimal4Value::FromDouble(9, 0, 999999999.123, false, &overflow); |
| EXPECT_FALSE(overflow); |
| |
| overflow = false; |
| Decimal4Value::FromDouble(9, 0, 1234567890.1, false, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| overflow = false; |
| Decimal8Value::FromDouble(9, 0, -1234567890.123, false, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| overflow = false; |
| Decimal8Value::FromDouble(10, 5, 99999.1234567, false, &overflow); |
| EXPECT_FALSE(overflow); |
| |
| overflow = false; |
| Decimal8Value::FromDouble(10, 5, 100000.1, false, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| overflow = false; |
| Decimal8Value::FromDouble(10, 5, -123456.123, false, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| overflow = false; |
| d16 = Decimal16Value::FromDouble(10, 10, 0.1234, true, &overflow); |
| EXPECT_FALSE(overflow); |
| VerifyToString(d16, 10, 10, "0.1234000000"); |
| |
| overflow = false; |
| Decimal16Value::FromDouble(10, 10, 1.1, true, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| overflow = false; |
| Decimal16Value::FromDouble(10, 10, -1.1, true, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| overflow = false; |
| Decimal16Value::FromDouble(10, 10, 0.99999999999, false, &overflow); |
| EXPECT_FALSE(overflow); |
| |
| overflow = false; |
| Decimal16Value::FromDouble(10, 10, 0.99999999999, true, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| overflow = false; |
| Decimal16Value::FromDouble(10, 10, -0.99999999999, false, &overflow); |
| EXPECT_FALSE(overflow); |
| |
| overflow = false; |
| Decimal16Value::FromDouble(10, 10, -0.99999999999, true, &overflow); |
| EXPECT_TRUE(overflow); |
| overflow = false; |
| |
| // Test half rounding behavior |
| d4 = Decimal4Value::FromDouble(1, 0, 0.499999999, true, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d4.value(), 0); |
| VerifyToString(d4, 1, 0, "0"); |
| |
| d4 = Decimal4Value::FromDouble(1, 0, 0.5, true, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d4.value(), 1); |
| VerifyToString(d4, 1, 0, "1"); |
| |
| d4 = Decimal4Value::FromDouble(1, 0, -0.499999999, true, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d4.value(), 0); |
| VerifyToString(d4, 1, 0, "0"); |
| |
| d4 = Decimal4Value::FromDouble(1, 0, -0.5, true, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(d4.value(), -1); |
| VerifyToString(d4, 1, 0, "-1"); |
| } |
| |
| TEST(DecimalTest, StringToDecimalBasic) { |
| StringToAllDecimals(" 1234", 10, 0, 1234, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("", 10, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals(" ", 10, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals(" 0 0 ", 10, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("0 0", 10, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("++0", 10, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("--0", 10, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("..0", 10, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("0..", 10, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals(".0.", 10, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("0.0.", 10, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("0-", 10, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("0+", 10, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("X", 10, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("+", 2, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("-", 2, 0, 0, StringParser::PARSE_FAILURE); |
| |
| StringToAllDecimals("1234", 10, 0, 1234, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("1234", 10, 2, 123400, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("-1234", 10, 2, -123400, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("123", 2, 0, 123, StringParser::PARSE_OVERFLOW); |
| StringToAllDecimals(" 12 ", 2, 0, 12, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("000", 2, 0, 0, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals(" . ", 2, 0, 0, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("-.", 2, 0, 0, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("1.", 2, 0, 1, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals(".1", 10, 2, 10, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("00012.3", 10, 2, 1230, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("-00012.3", 10, 2, -1230, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("0.00000", 6, 5, 0, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("1.00000", 6, 5, 100000, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("0.000000", 6, 5, 0, StringParser::PARSE_UNDERFLOW); |
| StringToAllDecimals("1.000000", 6, 5, 100000, StringParser::PARSE_UNDERFLOW); |
| StringToAllDecimals("1.000004", 6, 5, 100000, StringParser::PARSE_UNDERFLOW); |
| StringToAllDecimals("1.000005", 6, 5, |
| 100000, StringParser::PARSE_UNDERFLOW, |
| 100001, StringParser::PARSE_UNDERFLOW); |
| StringToAllDecimals("0.4", 5, 0, 0, StringParser::PARSE_UNDERFLOW); |
| StringToAllDecimals("0.5", 5, 0, |
| 0, StringParser::PARSE_UNDERFLOW, |
| 1, StringParser::PARSE_UNDERFLOW); |
| |
| StringToAllDecimals("123.45", 10, 2, 12345, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals(".45", 10, 2, 45, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("-.45", 10, 2, -45, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals(" 123.4 ", 10, 5, 12340000, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("-123.45", 10, 5, -12345000, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("-123.456", 10, 2, |
| -12345, StringParser::PARSE_UNDERFLOW, |
| -12346, StringParser::PARSE_UNDERFLOW); |
| |
| StringToAllDecimals("e", 2, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("E", 2, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("+E", 2, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals(".E", 2, 0, 0, StringParser::PARSE_FAILURE); |
| StringToAllDecimals(" 0 e 0 ", 10, 0, 100, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("0e 0", 10, 0, 100, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("0e1.0", 10, 0, 100, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("0e1.", 10, 0, 100, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("0e.1", 10, 0, 100, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("0ee0", 10, 0, 100, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("e1", 10, 0, 100, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("1e", 10, 0, 100, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("1.1.0e0", 10, 0, 100, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("1e1.0", 10, 0, 100, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("1..1e1", 10, 0, 100, StringParser::PARSE_FAILURE); |
| StringToAllDecimals("1e9999999999999999999", 10, 0, 0, StringParser::PARSE_OVERFLOW); |
| StringToAllDecimals("1e-38", 10, 2, 0, StringParser::PARSE_UNDERFLOW); |
| StringToAllDecimals("1e-38", 38, 38, 1, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("-1e-38", 38, 38, -1, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("1e-39", 38, 38, 0, StringParser::PARSE_UNDERFLOW); |
| StringToAllDecimals("-1e-39", 38, 38, 0, StringParser::PARSE_UNDERFLOW); |
| StringToAllDecimals("1e-9999999999999999999", 10, 0, 0, StringParser::PARSE_UNDERFLOW); |
| StringToAllDecimals("-1e-9999999999999999999", 10, 0, 0, StringParser::PARSE_UNDERFLOW); |
| |
| StringToAllDecimals(" 1e0 ", 10, 2, 100, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("-1e0", 10, 2, -100, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("1e-0", 10, 2, 100, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals(" 1e2 ", 10, 2, 10000, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("-1e-2", 10, 2, -1, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals(".011e3 ", 2, 0, 11, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals(".00011e5 ", 2, 0, 11, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("110e-1 ", 2, 0, 11, StringParser::PARSE_UNDERFLOW); |
| StringToAllDecimals("1.10e1 ", 2, 0, 11, StringParser::PARSE_UNDERFLOW); |
| StringToAllDecimals("1.10e3 ", 2, 0, 11, StringParser::PARSE_OVERFLOW); |
| } |
| |
| TEST(DecimalTest, StringToDecimalLarge) { |
| StringToAllDecimals("1", 1, 0, 1, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals("-1", 1, 0, -1, StringParser::PARSE_SUCCESS); |
| StringToAllDecimals(".1", 1, 0, 0, StringParser::PARSE_UNDERFLOW); |
| StringToAllDecimals("10", 1, 0, 10, StringParser::PARSE_OVERFLOW); |
| StringToAllDecimals("-10", 1, 0, -10, StringParser::PARSE_OVERFLOW); |
| |
| VerifyParse(".1234567890", 10, 10, |
| Decimal8Value(1234567890L), StringParser::PARSE_SUCCESS); |
| VerifyParse("-.1234567890", 10, 10, |
| Decimal8Value(-1234567890L), StringParser::PARSE_SUCCESS); |
| VerifyParse(".12345678900", 10, 10, |
| Decimal8Value(1234567890L), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("-.12345678900", 10, 10, |
| Decimal8Value(-1234567890L), StringParser::PARSE_UNDERFLOW); |
| VerifyParse(".1234567890", 10, 10, |
| Decimal16Value(1234567890L), StringParser::PARSE_SUCCESS); |
| VerifyParse("-.1234567890", 10, 10, |
| Decimal16Value(-1234567890L), StringParser::PARSE_SUCCESS); |
| VerifyParse(".12345678900", 10, 10, |
| Decimal16Value(1234567890L), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("-.12345678900", 10, 10, |
| Decimal16Value(-1234567890L), StringParser::PARSE_UNDERFLOW); |
| |
| // Up to 8 digits with 5 before the decimal and 3 after. |
| VerifyParse("12345.678", 8, 3, |
| Decimal8Value(12345678L), StringParser::PARSE_SUCCESS); |
| VerifyParse("-12345.678", 8, 3, |
| Decimal8Value(-12345678L), StringParser::PARSE_SUCCESS); |
| VerifyParse("123456.78", 8, 3, |
| Decimal8Value(12345678L), StringParser::PARSE_OVERFLOW); |
| VerifyParse("1234.5678", 8, 3, |
| Decimal8Value(1234567L), StringParser::PARSE_UNDERFLOW, |
| Decimal8Value(1234568L), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("12345.678", 8, 3, |
| Decimal16Value(12345678L), StringParser::PARSE_SUCCESS); |
| VerifyParse("-12345.678", 8, 3, |
| Decimal16Value(-12345678L), StringParser::PARSE_SUCCESS); |
| VerifyParse("123456.78", 8, 3, |
| Decimal16Value(12345678L), StringParser::PARSE_OVERFLOW); |
| VerifyParse("1234.5678", 8, 3, |
| Decimal16Value(1234567L), StringParser::PARSE_UNDERFLOW, |
| Decimal16Value(1234568L), StringParser::PARSE_UNDERFLOW); |
| |
| // Test max unscaled value for each of the decimal types. |
| VerifyParse("999999999", 9, 0, |
| Decimal4Value(999999999), StringParser::PARSE_SUCCESS); |
| VerifyParse("99999.9999", 9, 4, |
| Decimal4Value(999999999), StringParser::PARSE_SUCCESS); |
| VerifyParse("0.999999999", 9, 9, |
| Decimal4Value(999999999), StringParser::PARSE_SUCCESS); |
| VerifyParse("-999999999", 9, 0, |
| Decimal4Value(-999999999), StringParser::PARSE_SUCCESS); |
| VerifyParse("-99999.9999", 9, 4, |
| Decimal4Value(-999999999), StringParser::PARSE_SUCCESS); |
| VerifyParse("-0.999999999", 9, 9, |
| Decimal4Value(-999999999), StringParser::PARSE_SUCCESS); |
| VerifyParse("1000000000", 9, 0, |
| Decimal4Value(0), StringParser::PARSE_OVERFLOW); |
| VerifyParse("-1000000000", 9, 0, |
| Decimal4Value(0), StringParser::PARSE_OVERFLOW); |
| |
| VerifyParse("999999999999999999", 18, 0, |
| Decimal8Value(999999999999999999ll), StringParser::PARSE_SUCCESS); |
| VerifyParse("999999.999999999999", 18, 12, |
| Decimal8Value(999999999999999999ll), StringParser::PARSE_SUCCESS); |
| VerifyParse(".999999999999999999", 18, 18, |
| Decimal8Value(999999999999999999ll), StringParser::PARSE_SUCCESS); |
| VerifyParse("-999999999999999999", 18, 0, |
| Decimal8Value(-999999999999999999ll), StringParser::PARSE_SUCCESS); |
| VerifyParse("-999999.999999999999", 18, 12, |
| Decimal8Value(-999999999999999999ll), StringParser::PARSE_SUCCESS); |
| VerifyParse("-.999999999999999999", 18, 18, |
| Decimal8Value(-999999999999999999ll), StringParser::PARSE_SUCCESS); |
| VerifyParse("1000000000000000000", 18, 0, |
| Decimal8Value(0), StringParser::PARSE_OVERFLOW); |
| VerifyParse("01000000000000000000", 18, 0, |
| Decimal8Value(0), StringParser::PARSE_OVERFLOW); |
| |
| int128_t result = MAX_UNSCALED_DECIMAL16; |
| VerifyParse("99999999999999999999999999999999999999", |
| 38, 0, Decimal16Value(result), StringParser::PARSE_SUCCESS); |
| VerifyParse("99999999999999999999999999999999999999e1", |
| 38, 0, Decimal16Value(result), StringParser::PARSE_OVERFLOW); |
| VerifyParse("999999999999999999999999999999999999990e-1", |
| 38, 0, Decimal16Value(result), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("999999999999999999999999999999999.99999", |
| 38, 5, Decimal16Value(result), StringParser::PARSE_SUCCESS); |
| VerifyParse(".99999999999999999999999999999999999999", |
| 38, 38, Decimal16Value(result), StringParser::PARSE_SUCCESS); |
| VerifyParse("-99999999999999999999999999999999999999", |
| 38, 0, Decimal16Value(-result), StringParser::PARSE_SUCCESS); |
| VerifyParse("-999999999999999999999999999999999.99999", |
| 38, 5, Decimal16Value(-result), StringParser::PARSE_SUCCESS); |
| VerifyParse("-.99999999999999999999999999999999999999", |
| 38, 38, Decimal16Value(-result), StringParser::PARSE_SUCCESS); |
| VerifyParse("-.99999999999999999999999999999999999999e1", |
| 38, 38, Decimal16Value(-result), StringParser::PARSE_OVERFLOW); |
| VerifyParse("-.999999999999999999999999999999999999990e-1", 38, 38, |
| Decimal16Value(-result / 10), StringParser::PARSE_UNDERFLOW, |
| Decimal16Value(-result / 10 - 1), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("-.999999999999999999999999999999999999990000000000000000e-20", 38, 38, |
| Decimal16Value(-result / DecimalUtil::GetScaleMultiplier<int128_t>(20)), |
| StringParser::PARSE_UNDERFLOW, |
| Decimal16Value(-result / DecimalUtil::GetScaleMultiplier<int128_t>(20) - 1), |
| StringParser::PARSE_UNDERFLOW); |
| VerifyParse("100000000000000000000000000000000000000", |
| 38, 0, Decimal16Value(0), StringParser::PARSE_OVERFLOW); |
| VerifyParse("-100000000000000000000000000000000000000", |
| 38, 0, Decimal16Value(0), StringParser::PARSE_OVERFLOW); |
| |
| // Rounding tests. |
| VerifyParse("555.554", 5, 2, Decimal4Value(55555), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("555.555", 5, 2, |
| Decimal4Value(55555), StringParser::PARSE_UNDERFLOW, |
| Decimal4Value(55556), StringParser::PARSE_UNDERFLOW); |
| // Too many digits to the left of the dot so we overflow. |
| VerifyParse("555.555e1", 5, 2, Decimal4Value(0), StringParser::PARSE_OVERFLOW); |
| VerifyParse("-555.555e1", 5, 2, Decimal4Value(0), StringParser::PARSE_OVERFLOW); |
| VerifyParse("5555.555", 5, 2, Decimal16Value(0), StringParser::PARSE_OVERFLOW); |
| VerifyParse("-555.555", 5, 2, |
| Decimal4Value(-55555), StringParser::PARSE_UNDERFLOW, |
| Decimal4Value(-55556), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("-5555.555", 5, 2, Decimal16Value(0), StringParser::PARSE_OVERFLOW); |
| VerifyParse("5555.555e-1", 5, 2, |
| Decimal4Value(55555), StringParser::PARSE_UNDERFLOW, |
| Decimal4Value(55556), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("55555.555e-1", 5, 2, Decimal16Value(0), StringParser::PARSE_OVERFLOW); |
| // Too many digits to the right of the dot and not enough to the left. Rounding via |
| // ScaleDownAndRound(). |
| VerifyParse("5.55444", 5, 2, Decimal4Value(555), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("5.55555", 5, 2, |
| Decimal4Value(555), StringParser::PARSE_UNDERFLOW, |
| Decimal4Value(556), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("5.555e-9", 5, 2, Decimal4Value(0), StringParser::PARSE_UNDERFLOW); |
| // The number of digits to the left of the dot equals to precision - scale. Rounding |
| // by adding 1 if the first truncated digit is greater or equal to 5. |
| VerifyParse("555.554", 5, 2, Decimal4Value(55555), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("555.555", 5, 2, |
| Decimal4Value(55555), StringParser::PARSE_UNDERFLOW, |
| Decimal4Value(55556), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("5.55554e2", 5, 2, Decimal4Value(55555), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("5.55555e2", 5, 2, |
| Decimal4Value(55555), StringParser::PARSE_UNDERFLOW, |
| Decimal4Value(55556), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("55555.4e-2", 5, 2, Decimal4Value(55555), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("55555.5e-2", 5, 2, |
| Decimal4Value(55555), StringParser::PARSE_UNDERFLOW, |
| Decimal4Value(55556), StringParser::PARSE_UNDERFLOW); |
| // Rounding causes overflow. |
| VerifyParse("999.994", 5, 2, Decimal4Value(99999), StringParser::PARSE_UNDERFLOW); |
| VerifyParse("999.995", 5, 2, |
| Decimal4Value(99999), StringParser::PARSE_UNDERFLOW, |
| Decimal4Value(0), StringParser::PARSE_OVERFLOW); |
| VerifyParse("9.99995e2", 5, 2, |
| Decimal4Value(99999), StringParser::PARSE_UNDERFLOW, |
| Decimal4Value(0), StringParser::PARSE_OVERFLOW); |
| VerifyParse("99999.5e-2", 5, 2, |
| Decimal4Value(99999), StringParser::PARSE_UNDERFLOW, |
| Decimal4Value(0), StringParser::PARSE_OVERFLOW); |
| } |
| |
| TEST(DecimalTest, Overflow) { |
| bool overflow = false; |
| |
| Decimal16Value result; |
| Decimal16Value d_max(MAX_UNSCALED_DECIMAL16); |
| Decimal16Value two(2); |
| Decimal16Value one(1); |
| Decimal16Value zero(0); |
| |
| // Adding same sign |
| overflow = false; |
| d_max.Add<int128_t>(0, one, 0, 38, 0, false, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| overflow = false; |
| one.Add<int128_t>(0, d_max, 0, 38, 0, false, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| overflow = false; |
| d_max.Add<int128_t>(0, d_max, 0, 38, 0, false, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| overflow = false; |
| result = d_max.Add<int128_t>(0, zero, 0, 38, 0, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_TRUE(result.value() == d_max.value()); |
| |
| // Subtracting same sign |
| overflow = false; |
| result = d_max.Subtract<int128_t>(0, one, 0, 38, 0, false, &overflow); |
| EXPECT_FALSE(overflow); |
| |
| overflow = false; |
| EXPECT_TRUE(result.value() == d_max.value() - 1); |
| result = one.Subtract<int128_t>(0, d_max, 0, 38, 0, false, &overflow); |
| EXPECT_FALSE(overflow); |
| |
| overflow = false; |
| EXPECT_TRUE(result.value() == -(d_max.value() - 1)); |
| result = d_max.Subtract<int128_t>(0, d_max, 0, 38, 0, false, &overflow); |
| EXPECT_FALSE(overflow); |
| |
| overflow = false; |
| EXPECT_TRUE(result.value() == 0); |
| result = d_max.Subtract<int128_t>(0, zero, 0, 38, 0, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_TRUE(result.value() == d_max.value()); |
| |
| // Adding different sign |
| overflow = false; |
| result = d_max.Add<int128_t>(0, -one, 0, 38, 0, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_TRUE(result.value() == d_max.value() - 1); |
| |
| overflow = false; |
| result = one.Add<int128_t>(0, -d_max, 0, 38, 0, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_TRUE(result.value() == -(d_max.value() - 1)); |
| |
| overflow = false; |
| result = d_max.Add<int128_t>(0, -d_max, 0, 38, 0, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_TRUE(result.value() == 0); |
| |
| overflow = false; |
| result = d_max.Add<int128_t>(0, -zero, 0, 38, 0, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_TRUE(result.value() == d_max.value()); |
| |
| // Subtracting different sign |
| overflow = false; |
| d_max.Subtract<int128_t>(0, -one, 0, 38, 0, false, &overflow); |
| EXPECT_TRUE(overflow); |
| one.Subtract<int128_t>(0, -d_max, 0, 38, 0, false, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| overflow = false; |
| d_max.Subtract<int128_t>(0, -d_max, 0, 38, 0, false, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| overflow = false; |
| result = d_max.Subtract<int128_t>(0, -zero, 0, 38, 0, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_TRUE(result.value() == d_max.value()); |
| |
| // Multiply |
| overflow = false; |
| result = d_max.Multiply<int128_t>(0, one, 0, 38, 0, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_TRUE(result.value() == d_max.value()); |
| |
| overflow = false; |
| result = d_max.Multiply<int128_t>(0, -one, 0, 38, 0, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_TRUE(result.value() == -d_max.value()); |
| |
| overflow = false; |
| result = d_max.Multiply<int128_t>(0, two, 0, 38, 0, false, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| overflow = false; |
| result = d_max.Multiply<int128_t>(0, -two, 0, 38, 0, false, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| overflow = false; |
| result = d_max.Multiply<int128_t>(0, d_max, 0, 38, 0, false, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| overflow = false; |
| result = d_max.Multiply<int128_t>(0, -d_max, 0, 38, 0, false, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| // Multiply by 0 |
| overflow = false; |
| result = zero.Multiply<int128_t>(0, one, 0, 38, 0, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_TRUE(result.value() == 0); |
| |
| overflow = false; |
| result = one.Multiply<int128_t>(0, zero, 0, 38, 0, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_TRUE(result.value() == 0); |
| |
| overflow = false; |
| result = zero.Multiply<int128_t>(0, zero, 0, 38, 0, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_TRUE(result.value() == 0); |
| |
| // Adding any value with scale to (38, 0) will overflow if the most significant |
| // digit is set. |
| overflow = false; |
| result = d_max.Add<int128_t>(0, zero, 1, 38, 1, false, &overflow); |
| EXPECT_TRUE(overflow); |
| |
| // Add 37 9's (with scale 0) |
| Decimal16Value d3(MAX_UNSCALED_DECIMAL16 / 10); |
| overflow = false; |
| result = d3.Add<int128_t>(0, zero, 1, 38, 1, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(result.value(), MAX_UNSCALED_DECIMAL16 - 9); |
| |
| overflow = false; |
| result = d3.Add<int128_t>(0, one, 1, 38, 1, false, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_EQ(result.value(), MAX_UNSCALED_DECIMAL16 - 8); |
| |
| // Mod |
| overflow = false; |
| bool is_nan; |
| result = d3.Mod<int128_t>(0, d3, 20, 38, 20, false, &is_nan, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_FALSE(is_nan); |
| EXPECT_EQ(result.value(), 0); |
| |
| overflow = false; |
| result = d3.Mod<int128_t>(0, two, 0, 38, 0, false, &is_nan, &overflow); |
| EXPECT_FALSE(overflow); |
| EXPECT_FALSE(is_nan); |
| EXPECT_EQ(result.value(), MAX_UNSCALED_DECIMAL16 % 2); |
| |
| result = d3.Mod<int128_t>(0, zero, 1, 38, 1, false, &is_nan, &overflow); |
| EXPECT_TRUE(is_nan); |
| } |
| |
| // Overflow cases only need to test with Decimal16Value with max precision. In |
| // the other cases, the planner should have casted the values to this precision. |
| // Add/Subtract/Mod cannot overflow the scale. With division, we always handle the case |
| // where the result scale needs to be adjusted. |
| TEST(DecimalTest, MultiplyScaleOverflow) { |
| bool overflow = false; |
| Decimal16Value x(1); |
| Decimal16Value y(3); |
| int max_scale = 38; |
| |
| // x = 0.<37 zeroes>1. y = 0.<37 zeroes>3 The result should be 0.<74 zeroes>3. |
| // Since this can't be represented, the result will truncate to 0. |
| Decimal16Value result = x.Multiply<int128_t>(max_scale, y, max_scale, 38, 38, false, &overflow); |
| EXPECT_TRUE(result.value() == 0); |
| EXPECT_FALSE(overflow); |
| |
| int scale_1 = 1; |
| int scale_37 = 37; |
| // x = 0.<36 zeroes>1, y = 0.3 |
| // The result should be 0.<37 zeroes>11, which would require scale = 39. |
| // The truncated version should 0.<37 zeroes>3. |
| result = x.Multiply<int128_t>(scale_37, y, scale_1, 38, 38, false, &overflow); |
| EXPECT_TRUE(result.value() == 3); |
| EXPECT_FALSE(overflow); |
| } |
| |
| // Test that unaligned decimal values are handled correctly. |
| TEST(DecimalTest, UnalignedValues) { |
| // Regression test for IMPALA-7473 that triggered a crash in release builds. |
| Decimal16Value aligned(12345); |
| uint8_t* unaligned_mem = reinterpret_cast<uint8_t*>(malloc(sizeof(Decimal16Value) + 9)); |
| memcpy(&unaligned_mem[9], &aligned, sizeof(aligned)); |
| Decimal16Value* unaligned = reinterpret_cast<Decimal16Value*>(&unaligned_mem[9]); |
| // VerifyToString() worked even prior to the bugfix because GCC happened to generate |
| // code without aligned load instructions. |
| VerifyToString(*unaligned, 28, 2, "123.45"); |
| // PrintValue() contained different generated code that wasn't safe for unaligned |
| // values. |
| stringstream ss; |
| RawValue::PrintValue(unaligned, ColumnType::CreateDecimalType(28, 2), 0, &ss); |
| EXPECT_EQ("123.45", ss.str()); |
| // Regression test for IMPALA-9781: Verify that operator=() works |
| *unaligned = 0; |
| __int128_t val = unaligned->value(); |
| EXPECT_EQ(val, 0); |
| free(unaligned_mem); |
| } |
| |
| enum Op { |
| ADD, |
| SUBTRACT, |
| MULTIPLY, |
| DIVIDE, |
| MOD, |
| }; |
| |
| // Implementation of decimal rules. This is handled in the planner in the normal |
| // execution paths. |
| ColumnType GetResultType(const ColumnType& t1, const ColumnType& t2, Op op, bool v2) { |
| // TODO: Implement V2 result types |
| switch (op) { |
| case ADD: |
| case SUBTRACT: |
| return ColumnType::CreateDecimalType( |
| max(t1.scale, t2.scale) + |
| max(t1.precision - t1.scale, t2.precision - t2.scale) + 1, |
| max(t1.scale, t2.scale)); |
| case MULTIPLY: |
| return ColumnType::CreateDecimalType( |
| t1.precision + t2.precision, t1.scale + t2.scale); |
| case DIVIDE: |
| if (v2) { |
| int result_scale = |
| max(ColumnType::MIN_ADJUSTED_SCALE, t1.scale + t2.precision + 1); |
| int result_precision =t1.precision - t1.scale + t2.scale + result_scale; |
| return ColumnType::CreateAdjustedDecimalType(result_precision, result_scale); |
| } else { |
| return ColumnType::CreateDecimalType( |
| min(ColumnType::MAX_PRECISION, |
| t1.precision - t1.scale + t2.scale + max(4, t1.scale + t2.precision + 1)), |
| min(ColumnType::MAX_PRECISION, max(4, t1.scale + t2.precision + 1))); |
| } |
| case MOD: |
| return ColumnType::CreateDecimalType( |
| min(t1.precision - t1.scale, t2.precision - t2.scale) + max(t1.scale, t2.scale), |
| max(t1.scale, t2.scale)); |
| default: |
| EXPECT_TRUE(false); |
| return ColumnType(); |
| } |
| } |
| |
| TEST(DecimalTest, ResultTypes) { |
| ColumnType t1 = ColumnType::CreateDecimalType(38, 10); |
| ColumnType t2 = ColumnType::CreateDecimalType(38, 38); |
| ColumnType t3 = ColumnType::CreateDecimalType(38, 0); |
| |
| auto r1 = GetResultType(t1, t2, DIVIDE, true); |
| EXPECT_EQ(r1.precision, 38); |
| EXPECT_EQ(r1.scale, 6); |
| |
| auto r2 = GetResultType(t1, t3, DIVIDE, true); |
| EXPECT_EQ(r2.precision, 38); |
| EXPECT_EQ(r2.scale, 10); |
| |
| auto r3 = GetResultType(t1, t2, DIVIDE, false); |
| EXPECT_EQ(r3.precision, 38); |
| EXPECT_EQ(r3.scale, 38); |
| } |
| |
| template<typename T> |
| void VerifyFuzzyEquals(const T& actual, const ColumnType& t, |
| double expected, bool overflow, double max_error = MAX_ERROR) { |
| double actual_d = actual.ToDouble(t.scale); |
| EXPECT_FALSE(overflow); |
| EXPECT_TRUE(fabs(actual_d - expected) < max_error) |
| << actual_d << " != " << expected; |
| } |
| |
| TEST(DecimalTest, BasicArithmetic) { |
| ColumnType t1 = ColumnType::CreateDecimalType(5, 4); |
| ColumnType t2 = ColumnType::CreateDecimalType(8, 3); |
| ColumnType t1_plus_2 = GetResultType(t1, t2, ADD, false); |
| ColumnType t1_times_2 = GetResultType(t1, t2, MULTIPLY, false); |
| |
| Decimal4Value d1(123456789); |
| Decimal4Value d2(23456); |
| Decimal4Value d3(-23456); |
| double d1_double = d1.ToDouble(t1.scale); |
| double d2_double = d2.ToDouble(t2.scale); |
| double d3_double = d3.ToDouble(t2.scale); |
| |
| bool overflow = false; |
| // TODO: what's the best way to author a bunch of tests like this? |
| VerifyFuzzyEquals(d1.Add<int64_t>( |
| t1.scale, d2, t2.scale, t1_plus_2.precision, t1_plus_2.scale, false, &overflow), |
| t1_plus_2, d1_double + d2_double, overflow); |
| VerifyFuzzyEquals(d1.Add<int64_t>( |
| t1.scale, d3, t2.scale, t1_plus_2.precision, t1_plus_2.scale, false, &overflow), |
| t1_plus_2, d1_double + d3_double, overflow); |
| VerifyFuzzyEquals(d1.Subtract<int64_t>( |
| t1.scale, d2, t2.scale, t1_plus_2.precision, t1_plus_2.scale, false, &overflow), |
| t1_plus_2, d1_double - d2_double, overflow); |
| VerifyFuzzyEquals(d1.Subtract<int64_t>( |
| t1.scale, d3, t2.scale, t1_plus_2.precision, t1_plus_2.scale, false, &overflow), |
| t1_plus_2, d1_double - d3_double, overflow); |
| VerifyFuzzyEquals(d1.Multiply<int128_t>( |
| t1.scale, d2, t2.scale, t1_times_2.precision, t1_times_2.scale, false, &overflow), |
| t1_times_2, d1_double * d2_double, overflow); |
| VerifyFuzzyEquals(d1.Multiply<int64_t>( |
| t1.scale, d3, t2.scale, t1_times_2.precision, t1_times_2.scale, false, &overflow), |
| t1_times_2, d1_double * d3_double, overflow); |
| } |
| |
| TEST(DecimalTest, Divide) { |
| // Exhaustively test precision and scale for 4 byte decimals. The logic errors tend |
| // to be by powers of 10 so not testing the other decimal types is okay. |
| Decimal4Value x(123456789); |
| Decimal4Value y(234); |
| for (int numerator_p = 1; numerator_p <= 9; ++numerator_p) { |
| for (int numerator_s = 0; numerator_s <= numerator_p; ++numerator_s) { |
| for (int denominator_p = 1; denominator_p <= 3; ++denominator_p) { |
| for (int denominator_s = 0; denominator_s <= denominator_p; ++denominator_s) { |
| for (int v2: { 0, 1 }) { |
| ColumnType t1 = ColumnType::CreateDecimalType(numerator_p, numerator_s); |
| ColumnType t2 = ColumnType::CreateDecimalType(denominator_p, denominator_s); |
| ColumnType t3 = GetResultType(t1, t2, DIVIDE, v2); |
| bool is_nan = false; |
| bool is_overflow = false; |
| Decimal8Value r = x.Divide<int64_t>( |
| t1.scale, y, t2.scale, t3.precision, t3.scale, true, &is_nan, &is_overflow); |
| double approx_x = x.ToDouble(t1.scale); |
| double approx_y = y.ToDouble(t2.scale); |
| double approx_r = r.ToDouble(t3.scale); |
| double expected_r = approx_x / approx_y; |
| |
| EXPECT_FALSE(is_nan); |
| EXPECT_FALSE(is_overflow); |
| if (fabs(approx_r - expected_r) > MAX_ERROR) { |
| LOG(ERROR) << approx_r << " " << expected_r; |
| LOG(ERROR) << x.ToString(t1) << "/" << y.ToString(t2) |
| << "=" << r.ToString(t3); |
| EXPECT_TRUE(false); |
| } |
| } |
| } |
| } |
| } |
| } |
| // Divide by 0 |
| bool is_nan = false; |
| bool is_overflow = false; |
| Decimal8Value r = x.Divide<int64_t>(0, Decimal4Value(0), 0, 38, 4, true, |
| &is_nan, &is_overflow); |
| EXPECT_TRUE(is_nan) << "Expected NaN, got: " << r; |
| EXPECT_FALSE(is_overflow); |
| |
| // In this case, we are dividing large precision decimals meaning the resulting |
| // decimal underflows. The resulting type is (38,38). |
| Decimal16Value x2(53994500); |
| Decimal16Value y2(5399450); |
| is_nan = false; |
| is_overflow = false; |
| x2.Divide<int128_t>(4, y2, 4, 38, 38, true, &is_nan, &is_overflow); |
| EXPECT_TRUE(is_overflow); |
| EXPECT_FALSE(is_nan); |
| } |
| |
| TEST(DecimalTest, DivideLargeScales) { |
| ColumnType t1 = ColumnType::CreateDecimalType(38, 8); |
| ColumnType t2 = ColumnType::CreateDecimalType(20, 0); |
| ColumnType t3 = GetResultType(t1, t2, DIVIDE, false); |
| StringParser::ParseResult result; |
| const char* data = "319391280635.61476055"; |
| Decimal16Value x = |
| StringParser::StringToDecimal<int128_t>(data, strlen(data), t1, false, &result); |
| Decimal16Value y(10000); |
| bool is_nan = false; |
| bool is_overflow = false; |
| Decimal16Value r = x.Divide<int128_t>(t1.scale, y, t2.scale, t3.precision, t3.scale, |
| true, &is_nan, &is_overflow); |
| VerifyToString(r, t3.precision, t3.scale, "31939128.06356147605500000000000000000"); |
| EXPECT_FALSE(is_nan); |
| EXPECT_FALSE(is_overflow); |
| |
| y = -y; |
| r = x.Divide<int128_t>(t1.scale, y, t2.scale, t3.precision, t3.scale, true, |
| &is_nan, &is_overflow); |
| VerifyToString(r, t3.precision, t3.scale, "-31939128.06356147605500000000000000000"); |
| EXPECT_FALSE(is_nan); |
| EXPECT_FALSE(is_overflow); |
| } |
| |
| template<typename T> |
| DecimalValue<T> RandDecimal(int max_precision) { |
| T val = 0; |
| int precision = rand() % max_precision; |
| for (int i = 0; i < precision; ++i) { |
| int digit = rand() % 10; |
| val = val * 10 + digit; |
| } |
| return DecimalValue<T>(rand() % 2 == 0 ? val : -val); |
| } |
| |
| int DoubleCompare(double x, double y) { |
| if (x < y) return -1; |
| if (x > y) return 1; |
| return 0; |
| } |
| |
| // Randomly test decimal operations, comparing the result with a double ground truth. |
| TEST(DecimalTest, RandTesting) { |
| int NUM_ITERS = 1000000; |
| int seed = time(0); |
| LOG(ERROR) << "Seed: " << seed; |
| for (int i = 0; i < NUM_ITERS; ++i) { |
| // TODO: double is too imprecise so we can't test with high scales. |
| int p1 = rand() % 12 + 1; |
| int s1 = rand() % min(4, p1); |
| int p2 = rand() % 12 + 1; |
| int s2 = rand() % min(4, p2); |
| |
| DecimalValue<int64_t> dec1 = RandDecimal<int64_t>(p1); |
| DecimalValue<int64_t> dec2 = RandDecimal<int64_t>(p2); |
| ColumnType t1 = ColumnType::CreateDecimalType(p1, s1); |
| ColumnType t2 = ColumnType::CreateDecimalType(p2, s2); |
| double t1_d = dec1.ToDouble(s1); |
| double t2_d = dec2.ToDouble(s2); |
| |
| ColumnType add_t = GetResultType(t1, t2, ADD, false); |
| |
| bool overflow = false; |
| VerifyFuzzyEquals(dec1.Add<int64_t>( |
| t1.scale, dec2, t2.scale, add_t.precision, add_t.scale, false, &overflow), |
| add_t, t1_d + t2_d, overflow); |
| VerifyFuzzyEquals(dec1.Subtract<int64_t>( |
| t1.scale, dec2, t2.scale, add_t.precision, add_t.scale, false, &overflow), |
| add_t, t1_d - t2_d, overflow); |
| if (overflow) continue; |
| |
| #if 0 |
| // multiply V2 result type not implemented yet |
| ColumnType multiply_t = GetResultType(t1, t2, MULTIPLY, true); |
| VerifyFuzzyEquals(dec1.Multiply<int64_t>( |
| t1.scale, dec2, t2.scale, multiply_t.scale), multiply_t, t1_d * t2_d); |
| #endif |
| // With rounding, we should be able to get much closer to real values |
| ColumnType divide_t = GetResultType(t1, t2, DIVIDE, true); |
| bool is_nan = false; |
| overflow = false; |
| auto result = dec1.Divide<int128_t>(t1.scale, dec2, t2.scale, divide_t.precision, |
| divide_t.scale, true, &is_nan, &overflow); |
| if (!is_nan && !overflow) |
| VerifyFuzzyEquals(result, divide_t, t1_d / t2_d, false, |
| pow(10, divide_t.precision - divide_t.scale - 6)); |
| EXPECT_EQ(is_nan, dec2.value() == 0); |
| EXPECT_EQ(dec1.Compare(t1.scale, dec2, t2.scale), DoubleCompare(t1_d, t2_d)); |
| EXPECT_TRUE(dec1.Compare(t1.scale, dec1, t1.scale) == 0); |
| EXPECT_TRUE(dec2.Compare(t2.scale, dec2, t2.scale) == 0); |
| } |
| } |
| |
| TEST(DecimalTest, PrecisionScaleValidation) { |
| // Valid precision and scale. |
| EXPECT_TRUE(ColumnType::ValidateDecimalParams(1, 0)); |
| EXPECT_TRUE(ColumnType::ValidateDecimalParams(1, 1)); |
| EXPECT_TRUE(ColumnType::ValidateDecimalParams(38, 38)); |
| EXPECT_TRUE(ColumnType::ValidateDecimalParams(38, 0)); |
| |
| // Out of range precision or scale. |
| EXPECT_FALSE(ColumnType::ValidateDecimalParams(3, -1)); |
| EXPECT_FALSE(ColumnType::ValidateDecimalParams(0, 0)); |
| EXPECT_FALSE(ColumnType::ValidateDecimalParams(39, 0)); |
| EXPECT_FALSE(ColumnType::ValidateDecimalParams(38, 39)); |
| |
| // Incompatible precision and scale. |
| EXPECT_FALSE(ColumnType::ValidateDecimalParams(15, 16)); |
| } |
| |
| } |
| |