You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by gr...@apache.org on 2018/02/02 22:19:03 UTC
[2/3] kudu git commit: KUDU-721: Support for Decimal type,
Part 1 (Server, C++ Client)
http://git-wip-us.apache.org/repos/asf/kudu/blob/7b6cc845/src/kudu/util/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/kudu/util/CMakeLists.txt b/src/kudu/util/CMakeLists.txt
index b107b73..82a5a02 100644
--- a/src/kudu/util/CMakeLists.txt
+++ b/src/kudu/util/CMakeLists.txt
@@ -128,6 +128,7 @@ set(UTIL_SRCS
cow_object.cc
crc.cc
debug-util.cc
+ decimal_util.cc
debug/trace_event_impl.cc
debug/trace_event_impl_constants.cc
debug/trace_event_synthetic_delay.cc
@@ -339,6 +340,7 @@ ADD_KUDU_TEST(callback_bind-test)
ADD_KUDU_TEST(countdown_latch-test)
ADD_KUDU_TEST(crc-test RUN_SERIAL true) # has a benchmark
ADD_KUDU_TEST(debug-util-test)
+ADD_KUDU_TEST(decimal_util-test)
ADD_KUDU_TEST(easy_json-test)
ADD_KUDU_TEST(env-test LABELS no_tsan)
ADD_KUDU_TEST(env_util-test)
http://git-wip-us.apache.org/repos/asf/kudu/blob/7b6cc845/src/kudu/util/decimal_util-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/util/decimal_util-test.cc b/src/kudu/util/decimal_util-test.cc
new file mode 100644
index 0000000..e8c9fc1
--- /dev/null
+++ b/src/kudu/util/decimal_util-test.cc
@@ -0,0 +1,73 @@
+// 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 <string>
+
+#include <gtest/gtest.h>
+
+#include "kudu/util/decimal_util.h"
+
+using std::string;
+
+namespace kudu {
+
+TEST(TestDecimalUtil, TestMaxUnscaledDecimal) {
+ ASSERT_EQ(9, MaxUnscaledDecimal(1));
+ ASSERT_EQ(99999, MaxUnscaledDecimal(5));
+ ASSERT_EQ(kMaxUnscaledDecimal32, MaxUnscaledDecimal(kMaxDecimal32Precision));
+ ASSERT_EQ(kMaxUnscaledDecimal64, MaxUnscaledDecimal(kMaxDecimal64Precision));
+ ASSERT_EQ(kMaxUnscaledDecimal128, MaxUnscaledDecimal(kMaxDecimal128Precision));
+}
+
+TEST(TestDecimalUtil, TestToString) {
+ ASSERT_EQ("999999999",
+ DecimalToString(kMaxUnscaledDecimal32, kDefaultDecimalScale));
+ ASSERT_EQ("0.999999999",
+ DecimalToString(kMaxUnscaledDecimal32, kMaxDecimal32Precision));
+ ASSERT_EQ("-999999999",
+ DecimalToString(kMinUnscaledDecimal32, kDefaultDecimalScale));
+ ASSERT_EQ("-0.999999999",
+ DecimalToString(kMinUnscaledDecimal32, kMaxDecimal32Precision));
+
+ ASSERT_EQ("999999999999999999",
+ DecimalToString(kMaxUnscaledDecimal64, kDefaultDecimalScale));
+ ASSERT_EQ("0.999999999999999999",
+ DecimalToString(kMaxUnscaledDecimal64, kMaxDecimal64Precision));
+ ASSERT_EQ("-999999999999999999",
+ DecimalToString(kMinUnscaledDecimal64, kDefaultDecimalScale));
+ ASSERT_EQ("-0.999999999999999999",
+ DecimalToString(kMinUnscaledDecimal64, kMaxDecimal64Precision));
+
+ ASSERT_EQ("99999999999999999999999999999999999999",
+ DecimalToString(kMaxUnscaledDecimal128, kDefaultDecimalScale));
+ ASSERT_EQ("0.99999999999999999999999999999999999999",
+ DecimalToString(kMaxUnscaledDecimal128, kMaxDecimal128Precision));
+ ASSERT_EQ("-99999999999999999999999999999999999999",
+ DecimalToString(kMinUnscaledDecimal128, kDefaultDecimalScale));
+ ASSERT_EQ("-0.99999999999999999999999999999999999999",
+ DecimalToString(kMinUnscaledDecimal128, kMaxDecimal128Precision));
+
+ ASSERT_EQ("0", DecimalToString(0, 0));
+ ASSERT_EQ("12345", DecimalToString(12345, 0));
+ ASSERT_EQ("-12345", DecimalToString(-12345, 0));
+ ASSERT_EQ("123.45", DecimalToString(12345, 2));
+ ASSERT_EQ("-123.45", DecimalToString(-12345, 2));
+ ASSERT_EQ("0.00012345", DecimalToString(12345, 8));
+ ASSERT_EQ("-0.00012345", DecimalToString(-12345, 8));
+}
+
+} // namespace kudu
http://git-wip-us.apache.org/repos/asf/kudu/blob/7b6cc845/src/kudu/util/decimal_util.cc
----------------------------------------------------------------------
diff --git a/src/kudu/util/decimal_util.cc b/src/kudu/util/decimal_util.cc
new file mode 100644
index 0000000..af81ae5
--- /dev/null
+++ b/src/kudu/util/decimal_util.cc
@@ -0,0 +1,83 @@
+// 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 "kudu/util/decimal_util.h"
+
+#include <string>
+
+#include <glog/logging.h>
+
+namespace kudu {
+
+using std::string;
+
+// Workaround for an ASAN build issue documented here:
+// https://bugs.llvm.org/show_bug.cgi?id=16404
+__attribute__((no_sanitize("undefined")))
+int128_t MaxUnscaledDecimal(int8_t precision) {
+ DCHECK_GE(precision, kMinDecimalPrecision);
+ DCHECK_LE(precision, kMaxDecimalPrecision);
+ int128_t result = 1;
+ for (; precision > 0; precision--) {
+ result = result * 10;
+ }
+ return result - 1;
+}
+
+// Workaround for an ASAN build issue documented here:
+// https://bugs.llvm.org/show_bug.cgi?id=16404
+__attribute__((no_sanitize("undefined")))
+string DecimalToString(int128_t d, int8_t scale) {
+ // 38 digits, 1 extra leading zero, decimal point,
+ // and sign are good for 128-bit or smaller decimals.
+ char local[41];
+ char *p = local + sizeof(local);
+ int128_t n = d < 0? -d : d;
+ int position = 0;
+ while (n) {
+ // Print the decimal in the scale position.
+ // No decimal is output when scale is 0.
+ if (scale != 0 && position == scale) {
+ *--p = '.';
+ }
+ // Unroll the next digits.
+ *--p = '0' + n % 10;
+ n /= 10;
+ position++;
+ }
+ // True if the value is between 1 and -1.
+ bool fractional = position <= scale;
+ // Pad with zeros until the scale
+ while (position < scale) {
+ *--p = '0';
+ position++;
+ }
+ // Add leading "0.".
+ if (fractional) {
+ if (d != 0) {
+ *--p = '.';
+ }
+ *--p = '0';
+ }
+ // Add sign for negative values.
+ if (d < 0) {
+ *--p = '-';
+ }
+ return string(p, local + sizeof(local));
+}
+
+} // namespace kudu
http://git-wip-us.apache.org/repos/asf/kudu/blob/7b6cc845/src/kudu/util/decimal_util.h
----------------------------------------------------------------------
diff --git a/src/kudu/util/decimal_util.h b/src/kudu/util/decimal_util.h
new file mode 100644
index 0000000..271bc0e
--- /dev/null
+++ b/src/kudu/util/decimal_util.h
@@ -0,0 +1,65 @@
+// 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.
+
+#pragma once
+
+#include <stdint.h>
+#include <string>
+
+#include "kudu/util/int128.h"
+
+namespace kudu {
+ // Maximum precision and absolute value of a Decimal that can be stored
+ // in 4 bytes.
+ static const int8_t kMaxDecimal32Precision = 9;
+ static const int32_t kMaxUnscaledDecimal32 = 999999999; // 9 9's
+ static const int32_t kMinUnscaledDecimal32 = -kMaxUnscaledDecimal32; // 9 9's
+
+ // Maximum precision and absolute value of a valid Decimal can be
+ // stored in 8 bytes.
+ static const int8_t kMaxDecimal64Precision = 18;
+ static const int64_t kMaxUnscaledDecimal64 = 999999999999999999; // 18 9's
+ static const int64_t kMinUnscaledDecimal64 = -kMaxUnscaledDecimal64; // 18 9's
+
+ // Maximum precision and absolute value of a valid Decimal can be
+ // stored in 16 bytes.
+ static const int8_t kMaxDecimal128Precision = 38;
+ // Hacky calculation because int128 literals are not supported.
+ static const int128_t kMaxUnscaledDecimal128 =
+ (((static_cast<int128_t>(999999999999999999) * 1000000000000000000) +
+ 999999999999999999) * 100) + 99; // 38 9's
+ static const int128_t kMinUnscaledDecimal128 = -kMaxUnscaledDecimal128;
+
+ // Minimum and maximum precision for any Decimal.
+ static const int8_t kMinDecimalPrecision = 1;
+ static const int8_t kMaxDecimalPrecision = kMaxDecimal128Precision;
+ // Maximum absolute value for any Decimal.
+ static const int128_t kMaxUnscaledDecimal = kMaxUnscaledDecimal128;
+ static const int128_t kMinUnscaledDecimal = kMinUnscaledDecimal128;
+
+ // Minimum scale for any Decimal.
+ static const int8_t kMinDecimalScale = 0;
+ static const int8_t kDefaultDecimalScale = 0;
+ // The maximum scale is the Decimal's precision.
+
+ // Returns the maximum unscaled decimal value that can be stored
+ // based on the precision
+ int128_t MaxUnscaledDecimal(int8_t precision);
+
+ std::string DecimalToString(int128_t value, int8_t scale);
+
+} // namespace kudu
http://git-wip-us.apache.org/repos/asf/kudu/blob/7b6cc845/src/kudu/util/int128-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/util/int128-test.cc b/src/kudu/util/int128-test.cc
index 175c4e4..30b84f4 100644
--- a/src/kudu/util/int128-test.cc
+++ b/src/kudu/util/int128-test.cc
@@ -31,12 +31,10 @@ namespace kudu {
TEST(TestInt128, TestOstreamSigned) {
int128_t INTEGERS[] = {0, -1, 1, -1234567890,
- -12345678901011121314151617181920212223_i128,
INT64_MIN, UINT64_MAX,
INT128_MIN,
INT128_MAX};
std::string STRINGS[] = {"0", "-1", "1", "-1234567890",
- "-12345678901011121314151617181920212223",
"-9223372036854775808", "18446744073709551615",
"-170141183460469231731687303715884105728",
"170141183460469231731687303715884105727"};
@@ -48,9 +46,9 @@ TEST(TestInt128, TestOstreamSigned) {
}
TEST(TestInt128, TestOstreamUnsigned) {
- uint128_t INTEGERS[] = {0, 1, 1234567890, 123456789101112131415161718192021222324_u128,
+ uint128_t INTEGERS[] = {0, 1, 1234567890,
UINT128_MIN, UINT128_MAX};
- string STRINGS[] = {"0", "1", "1234567890", "123456789101112131415161718192021222324",
+ string STRINGS[] = {"0", "1", "1234567890",
"0", "340282366920938463463374607431768211455"};
for (size_t i = 0; i < arraysize(INTEGERS); i++) {
std::ostringstream ss;
@@ -67,25 +65,4 @@ TEST(TestInt128, TestCasting) {
ASSERT_EQ(UINT128_MAX, castToMax);
}
-TEST(TestInt128, TestSuffix) {
- int128_t imax = 170141183460469231731687303715884105727_i128;
- ASSERT_EQ(INT128_MAX, imax);
-
- // Note: We can't represent the absolute minimum because numeric literals
- // never represent negative numbers and the - operator is applied after
- // the conversion to an int128_t.
- int128_t imin = -170141183460469231731687303715884105727_i128;
- ASSERT_EQ(INT128_MIN + 1, imin);
-
- uint128_t umax = 340282366920938463463374607431768211455_u128;
- ASSERT_EQ(UINT128_MAX, umax);
-
- uint128_t umin = 0_u128;
- ASSERT_EQ(UINT128_MIN, umin);
-
- // The values below will fail to compile
- // 170141183460469231731687303715884105728_i128; // Too large int128_t
- // 3340282366920938463463374607431768211456_u128; // Too large uint128_t
-}
-
} // namespace kudu
http://git-wip-us.apache.org/repos/asf/kudu/blob/7b6cc845/src/kudu/util/int128.h
----------------------------------------------------------------------
diff --git a/src/kudu/util/int128.h b/src/kudu/util/int128.h
index e78e450..96e51a8 100644
--- a/src/kudu/util/int128.h
+++ b/src/kudu/util/int128.h
@@ -22,8 +22,6 @@
#include <iostream>
-#include "kudu/gutil/mathlimits.h"
-
namespace kudu {
typedef unsigned __int128 uint128_t;
@@ -31,82 +29,10 @@ typedef signed __int128 int128_t;
// Note: We don't use numeric_limits because it can give incorrect
// values for __int128 and unsigned __int128.
-static const uint128_t UINT128_MIN = MathLimits<uint128_t>::kMin;
-static const uint128_t UINT128_MAX = MathLimits<uint128_t>::kMax;
-static const int128_t INT128_MIN = MathLimits<int128_t>::kMin;
-static const int128_t INT128_MAX = MathLimits<int128_t>::kMax;
-
-namespace int128Suffix {
-
-// Convert the characters 0 through 9 to their int value.
-constexpr uint128_t CharValue(char c) {
- return c - '0';
-}
-
-// Terminate the recursion.
-template <uint128_t VALUE>
-constexpr uint128_t ValidateU128Helper() {
- return true;
-}
-
-// Recurse the literal from left to right and validate the input.
-// Return true if the input is valid.
-template <uint128_t VALUE, char C, char... CS>
-constexpr bool ValidateU128Helper() {
- return (VALUE <= UINT128_MAX / 10) &&
- VALUE * 10 <= UINT128_MAX - CharValue(C) &&
- ValidateU128Helper<VALUE * 10 + CharValue(C), CS...>();
-}
-
-template <char... CS>
-constexpr bool ValidateU128() {
- return ValidateU128Helper<0, CS...>();
-}
-
-// Terminate the recursion.
-template <uint128_t VALUE>
-constexpr uint128_t MakeU128Helper() {
- return VALUE;
-}
-
-// Recurse the literal from left to right calculating the resulting value.
-// C is the current left most character, CS is the rest.
-template <uint128_t VALUE, char C, char... CS>
-constexpr uint128_t MakeU128Helper() {
- return MakeU128Helper<VALUE * 10 + CharValue(C), CS...>();
-}
-
-template <char... CS>
-constexpr uint128_t MakeU128() {
- return MakeU128Helper<0, CS...>();
-}
-
-template <char... CS>
-constexpr bool ValidateI128() {
- return ValidateU128Helper<0, CS...>() && MakeU128<CS...>() <= INT128_MAX;
-}
-
-} // namespace int128Suffix
-
-// User-defined literal "_u128" to support basic integer constants
-// for uint128_t until officially supported by compilers.
-template <char... CS>
-constexpr uint128_t operator"" _u128() {
- static_assert(int128Suffix::ValidateU128<CS...>(),
- "integer literal is too large to be represented in "
- "uint128_t integer type");
- return int128Suffix::MakeU128<CS...>();
-}
-
-// User-defined literal "_u128" to support basic integer constants
-// for int128_t until officially supported by compilers.
-template <char... CS>
-constexpr int128_t operator"" _i128() {
- static_assert(int128Suffix::ValidateI128<CS...>(),
- "integer literal is too large to be represented in "
- "int128_t integer type");
- return static_cast<int128_t>(int128Suffix::MakeU128<CS...>());
-}
+static const uint128_t UINT128_MIN = (uint128_t) 0;
+static const uint128_t UINT128_MAX = ((uint128_t) -1);
+static const int128_t INT128_MAX = ((int128_t)(UINT128_MAX >> 1));
+static const int128_t INT128_MIN = (-INT128_MAX - 1);
} // namespace kudu