/Users/deen/code/yugabyte-db/src/yb/util/decimal-test.cc
Line | Count | Source |
1 | | // Copyright (c) YugaByte, Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
4 | | // in compliance with the License. You may obtain a copy of the License at |
5 | | // |
6 | | // http://www.apache.org/licenses/LICENSE-2.0 |
7 | | // |
8 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
9 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
10 | | // or implied. See the License for the specific language governing permissions and limitations |
11 | | // under the License. |
12 | | // |
13 | | |
14 | | #include "yb/util/decimal.h" |
15 | | #include "yb/util/result.h" |
16 | | #include "yb/util/test_macros.h" |
17 | | #include "yb/util/test_util.h" |
18 | | |
19 | | namespace yb { |
20 | | namespace util { |
21 | | |
22 | | class DecimalTest : public YBTest { |
23 | | protected: |
24 | | // Note that the following test cases are only used for testing encodings. The other tests should |
25 | | // verify that Decimal representation is perfect, and only the conversion to the encoding and |
26 | | // its comparison need to be tested. |
27 | | const std::vector<std::string> test_cases = { |
28 | | // The purpose of these tests is to verify various aspects of comparisons for different cases. |
29 | | // The priority order for comparing two decimals is sign > exponent > mantissa. The mantissa |
30 | | // must be compared lexicographically while exponent must be compared in absolute value. |
31 | | |
32 | | // -2147483648 is the smallest signed int, so BigDecimal Encoding fails below this scale. |
33 | | "-9847.236776e+2147483654", // Note that the scale is -2147483648. |
34 | | "-9847.236780e+2147483653", |
35 | | // Testing numbers with close by digits to make sure comparison is correct. |
36 | | "-1.34", |
37 | | "-13.37e-1", |
38 | | "-13.34e-1", |
39 | | "-13.3e-1", |
40 | | // Checking the higher boundary of the scale. |
41 | | "-1.36e-2147483645", // Note that the scale is 2147483647, largest signed int. |
42 | | "-0", |
43 | | "0.05", |
44 | | "1.15", |
45 | | "1.2", |
46 | | "120e0", |
47 | | "1.2e+100", |
48 | | "2638.2e+3624" |
49 | | }; |
50 | | |
51 | | const std::vector<size_t> kComparableEncodingLengths = |
52 | | {10, 10, 3, 3, 3, 3, 7, 1, 2, 3, 2, 2, 3, 6}; |
53 | | const std::vector<size_t> kBigDecimalEncodingLengths = |
54 | | {9, 8, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 6}; |
55 | | }; |
56 | | |
57 | 1 | TEST_F(DecimalTest, TestToStringFunctions) { |
58 | 1 | std::string string; |
59 | 1 | Decimal decimal0({}, VarInt(0), /* is_positive = */ false); |
60 | 1 | Decimal decimal1({9, 0, 1, 2}, VarInt(-2), false); |
61 | 1 | Decimal decimal2({9, 0, 1, 2}, VarInt(2), true); |
62 | 1 | Decimal decimal3({9, 0, 1, 2}, VarInt(8), false); |
63 | 1 | Decimal decimal4( |
64 | 1 | {9, 0, 1, 2}, ASSERT_RESULT(VarInt::CreateFromString("-36546632732954564789")), true); |
65 | 1 | Decimal decimal5( |
66 | 1 | {9, 0, 1, 2}, ASSERT_RESULT(VarInt::CreateFromString("+36546632732954564789")), true); |
67 | | |
68 | 1 | EXPECT_EQ("[ + 10^+0 * 0. ]", decimal0.ToDebugString()); |
69 | 1 | EXPECT_OK(decimal0.ToPointString(&string)); |
70 | 1 | EXPECT_EQ("0", string); |
71 | 1 | EXPECT_EQ("0", decimal0.ToScientificString()); |
72 | 1 | EXPECT_EQ("0", decimal0.ToString()); |
73 | | |
74 | 1 | EXPECT_EQ("[ - 10^-2 * 0.9012 ]", decimal1.ToDebugString()); |
75 | 1 | EXPECT_OK(decimal1.ToPointString(&string)); |
76 | 1 | EXPECT_EQ("-0.009012", string); |
77 | 1 | EXPECT_EQ("-9.012e-3", decimal1.ToScientificString()); |
78 | 1 | EXPECT_EQ("-0.009012", decimal1.ToString()); |
79 | | |
80 | 1 | EXPECT_EQ("[ + 10^+2 * 0.9012 ]", decimal2.ToDebugString()); |
81 | 1 | EXPECT_OK(decimal2.ToPointString(&string)); |
82 | 1 | EXPECT_EQ("90.12", string); |
83 | 1 | EXPECT_EQ("9.012e+1", decimal2.ToScientificString()); |
84 | 1 | EXPECT_EQ("90.12", decimal2.ToString()); |
85 | | |
86 | 1 | EXPECT_EQ("[ - 10^+8 * 0.9012 ]", decimal3.ToDebugString()); |
87 | 1 | EXPECT_OK(decimal3.ToPointString(&string)); |
88 | 1 | EXPECT_EQ("-90120000", string); |
89 | 1 | EXPECT_EQ("-9.012e+7", decimal3.ToScientificString()); |
90 | 1 | EXPECT_EQ("-90120000", decimal3.ToString()); |
91 | | |
92 | 1 | EXPECT_EQ("[ + 10^-36546632732954564789 * 0.9012 ]", decimal4.ToDebugString()); |
93 | 1 | EXPECT_FALSE(decimal4.ToPointString(&string).ok()); |
94 | 1 | EXPECT_EQ("9.012e-36546632732954564790", decimal4.ToScientificString()); |
95 | 1 | EXPECT_EQ("9.012e-36546632732954564790", decimal4.ToString()); |
96 | | |
97 | 1 | EXPECT_EQ("[ + 10^+36546632732954564789 * 0.9012 ]", decimal5.ToDebugString()); |
98 | 1 | EXPECT_FALSE(decimal5.ToPointString(&string).ok()); |
99 | 1 | EXPECT_EQ("9.012e+36546632732954564788", decimal5.ToScientificString()); |
100 | 1 | EXPECT_EQ("9.012e+36546632732954564788", decimal5.ToString()); |
101 | 1 | } |
102 | | |
103 | 1 | TEST_F(DecimalTest, TestFromStringFunctions) { |
104 | 1 | Decimal decimal; |
105 | | |
106 | 1 | EXPECT_OK(decimal.FromString("0")); |
107 | 1 | EXPECT_EQ("[ + 10^+0 * 0. ]", decimal.ToDebugString()); |
108 | 1 | EXPECT_OK(decimal.FromString("+0")); |
109 | 1 | EXPECT_EQ("[ + 10^+0 * 0. ]", decimal.ToDebugString()); |
110 | 1 | EXPECT_OK(decimal.FromString("+00")); |
111 | 1 | EXPECT_EQ("[ + 10^+0 * 0. ]", decimal.ToDebugString()); |
112 | 1 | EXPECT_OK(decimal.FromString("0.1")); |
113 | 1 | EXPECT_EQ("[ + 10^+0 * 0.1 ]", decimal.ToDebugString()); |
114 | 1 | EXPECT_OK(decimal.FromString(".1")); |
115 | 1 | EXPECT_EQ("[ + 10^+0 * 0.1 ]", decimal.ToDebugString()); |
116 | 1 | EXPECT_OK(decimal.FromString("0.02")); |
117 | 1 | EXPECT_EQ("[ + 10^-1 * 0.2 ]", decimal.ToDebugString()); |
118 | 1 | EXPECT_OK(decimal.FromString("12.02")); |
119 | 1 | EXPECT_EQ("[ + 10^+2 * 0.1202 ]", decimal.ToDebugString()); |
120 | 1 | EXPECT_OK(decimal.FromString("+0120.")); |
121 | 1 | EXPECT_EQ("[ + 10^+3 * 0.12 ]", decimal.ToDebugString()); |
122 | 1 | EXPECT_OK(decimal.FromString("-0")); |
123 | 1 | EXPECT_EQ("[ + 10^+0 * 0. ]", decimal.ToDebugString()); |
124 | 1 | EXPECT_OK(decimal.FromString("-0.0")); |
125 | 1 | EXPECT_EQ("[ + 10^+0 * 0. ]", decimal.ToDebugString()); |
126 | 1 | EXPECT_OK(decimal.FromString("-9.012e-4")); |
127 | 1 | EXPECT_EQ("[ - 10^-3 * 0.9012 ]", decimal.ToDebugString()); |
128 | 1 | EXPECT_OK(decimal.FromString("9.012e-36546632732954564791")); |
129 | 1 | EXPECT_EQ("[ + 10^-36546632732954564790 * 0.9012 ]", decimal.ToDebugString()); |
130 | | |
131 | 1 | EXPECT_FALSE(decimal.FromString("").ok()); |
132 | 1 | EXPECT_FALSE(decimal.FromString("-").ok()); |
133 | 1 | EXPECT_FALSE(decimal.FromString("1.1a").ok()); |
134 | 1 | EXPECT_FALSE(decimal.FromString("1.1a1").ok()); |
135 | 1 | EXPECT_FALSE(decimal.FromString("1.1e").ok()); |
136 | 1 | EXPECT_FALSE(decimal.FromString("1.1e1a2").ok()); |
137 | 1 | } |
138 | | |
139 | 1 | TEST_F(DecimalTest, IsIntegerTest) { |
140 | 1 | EXPECT_TRUE(Decimal({}, VarInt(0), false).is_integer()); |
141 | | |
142 | 1 | EXPECT_FALSE(Decimal({3}, VarInt(-1), false).is_integer()); |
143 | 1 | EXPECT_FALSE(Decimal({3}, VarInt(0), false).is_integer()); |
144 | 1 | EXPECT_TRUE(Decimal({3}, VarInt(1), false).is_integer()); |
145 | 1 | auto big_positive = ASSERT_RESULT(VarInt::CreateFromString("328763771921201932786301")); |
146 | 1 | auto big_negative = ASSERT_RESULT(VarInt::CreateFromString("-328763771921201932786301")); |
147 | 1 | EXPECT_TRUE(Decimal({3}, big_positive, false).is_integer()); |
148 | 1 | EXPECT_FALSE(Decimal( |
149 | 1 | {3}, big_negative, false).is_integer()); |
150 | | |
151 | 1 | EXPECT_FALSE(Decimal({3, 0, 7, 8}, VarInt(-1), false).is_integer()); |
152 | 1 | EXPECT_FALSE(Decimal({3, 0, 7, 8}, VarInt(3), false).is_integer()); |
153 | 1 | EXPECT_TRUE(Decimal({3, 0, 7, 8}, VarInt(4), false).is_integer()); |
154 | 1 | EXPECT_TRUE(Decimal({3, 0, 7, 8}, big_positive, false).is_integer()); |
155 | 1 | EXPECT_FALSE(Decimal({3, 0, 7, 8}, big_negative, false).is_integer()); |
156 | 1 | } |
157 | | |
158 | 1 | TEST_F(DecimalTest, TestDoubleConversions) { |
159 | | // Note: Rounding errors are expected |
160 | | |
161 | 1 | auto dbl = Decimal("12.301").ToDouble(); |
162 | 1 | EXPECT_OK(dbl); |
163 | 1 | EXPECT_EQ("1.2301000000000000156e+1", Decimal(*dbl).ToString()); |
164 | | |
165 | 1 | EXPECT_OK(dbl = Decimal("-0").ToDouble()); |
166 | 1 | EXPECT_EQ("0", Decimal(*dbl).ToString()); |
167 | | |
168 | 1 | EXPECT_OK(dbl = Decimal("1236.8642261937127309271040921").ToDouble()); |
169 | 1 | EXPECT_EQ("1.2368642261937127387e+3", Decimal(*dbl).ToString()); |
170 | | |
171 | 1 | EXPECT_OK(dbl = Decimal("1.236864226e3").ToDouble()); |
172 | 1 | EXPECT_EQ("1.2368642259999999169e+3", Decimal(*dbl).ToString()); |
173 | | |
174 | | // Test large exponent |
175 | 1 | EXPECT_OK(dbl = Decimal("1.236864226e-33").ToDouble()); |
176 | 1 | EXPECT_EQ("1.2368642260000000385e-33", Decimal(*dbl).ToString()); |
177 | | |
178 | | // Exponent too large |
179 | 1 | EXPECT_NOT_OK(Decimal("1.236864226e-782323").ToDouble()); |
180 | | |
181 | 1 | Decimal decimal; |
182 | | |
183 | 1 | EXPECT_OK(decimal.FromDouble(std::numeric_limits<double>::epsilon())); |
184 | 1 | EXPECT_OK(dbl = decimal.ToDouble()); |
185 | 1 | EXPECT_EQ(std::numeric_limits<double>::epsilon(), *dbl); |
186 | 1 | EXPECT_EQ("2.2204460492503130808e-16", decimal.ToString()); |
187 | | |
188 | 1 | EXPECT_OK(decimal.FromDouble(std::numeric_limits<double>::lowest())); |
189 | 1 | EXPECT_OK(dbl = decimal.ToDouble()); |
190 | 1 | EXPECT_EQ(std::numeric_limits<double>::lowest(), *dbl); |
191 | 1 | EXPECT_EQ("-1.7976931348623157081e+308", decimal.ToString()); |
192 | | |
193 | 1 | EXPECT_OK(decimal.FromDouble(std::numeric_limits<double>::max())); |
194 | 1 | EXPECT_OK(dbl = decimal.ToDouble()); |
195 | 1 | EXPECT_EQ(std::numeric_limits<double>::max(), *dbl); |
196 | 1 | EXPECT_EQ("1.7976931348623157081e+308", decimal.ToString()); |
197 | | |
198 | | // Can convert from denorm values. |
199 | 1 | EXPECT_OK(decimal.FromDouble(std::numeric_limits<double>::denorm_min())); |
200 | | // Can convert to denorm values. |
201 | 1 | EXPECT_OK(decimal.ToDouble()); |
202 | 1 | EXPECT_EQ("4.9406564584124654418e-324", decimal.ToString()); |
203 | | |
204 | 1 | EXPECT_TRUE(decimal.FromDouble(std::numeric_limits<double>::infinity()).IsCorruption()); |
205 | 1 | EXPECT_TRUE(decimal.FromDouble(-std::numeric_limits<double>::infinity()).IsCorruption()); |
206 | 1 | EXPECT_TRUE(decimal.FromDouble(std::numeric_limits<double>::signaling_NaN()).IsCorruption()); |
207 | 1 | EXPECT_TRUE(decimal.FromDouble(std::numeric_limits<double>::quiet_NaN()).IsCorruption()); |
208 | 1 | } |
209 | | |
210 | 1 | TEST_F(DecimalTest, TestComparableEncoding) { |
211 | 1 | std::vector<Decimal> test_decimals; |
212 | 1 | std::vector<std::string> encoded_strings; |
213 | 1 | std::vector<Decimal> decoded_decimals; |
214 | 15 | for (size_t i = 0; i < test_cases.size(); i++) { |
215 | 14 | SCOPED_TRACE(Format("Index: $0, value: $1", i, test_cases[i])); |
216 | 14 | test_decimals.emplace_back(test_cases[i]); |
217 | 14 | encoded_strings.push_back(test_decimals[i].EncodeToComparable()); |
218 | 14 | EXPECT_EQ(kComparableEncodingLengths[i], encoded_strings[i].size()); |
219 | 14 | decoded_decimals.emplace_back(); |
220 | 14 | size_t length; |
221 | 14 | EXPECT_OK(decoded_decimals[i].DecodeFromComparable(encoded_strings[i], &length)); |
222 | 14 | EXPECT_EQ(kComparableEncodingLengths[i], length); |
223 | 14 | EXPECT_EQ(test_decimals[i], decoded_decimals[i]); |
224 | 14 | if (i > 0) { |
225 | 13 | EXPECT_GT(decoded_decimals[i], decoded_decimals[i-1]); |
226 | 13 | EXPECT_GT(decoded_decimals[i], test_decimals[i-1]); |
227 | 13 | EXPECT_GT(test_decimals[i], decoded_decimals[i-1]); |
228 | 13 | EXPECT_GT(test_decimals[i], test_decimals[i-1]); |
229 | 13 | EXPECT_GT(encoded_strings[i], encoded_strings[i-1]); |
230 | 13 | } |
231 | 14 | } |
232 | 1 | } |
233 | | |
234 | 1 | TEST_F(DecimalTest, TestBigDecimalEncoding) { |
235 | 1 | std::vector<Decimal> test_decimals; |
236 | 1 | std::vector<std::string> encoded_strings; |
237 | 1 | std::vector<Decimal> decoded_decimals; |
238 | 1 | bool is_out_of_range = false; |
239 | 15 | for (size_t i = 0; i < test_cases.size(); i++) { |
240 | 14 | SCOPED_TRACE(Format("Index: $0, value: $1", i, test_cases[i])); |
241 | 14 | test_decimals.emplace_back(test_cases[i]); |
242 | 14 | encoded_strings.push_back(test_decimals[i].EncodeToSerializedBigDecimal(&is_out_of_range)); |
243 | 14 | EXPECT_FALSE(is_out_of_range); |
244 | 14 | EXPECT_EQ(kBigDecimalEncodingLengths[i], encoded_strings[i].size()); |
245 | 14 | decoded_decimals.emplace_back(); |
246 | 14 | EXPECT_OK(decoded_decimals[i].DecodeFromSerializedBigDecimal(encoded_strings[i])); |
247 | 14 | EXPECT_EQ(decoded_decimals[i], test_decimals[i]); |
248 | 14 | if (i > 0) { |
249 | 13 | EXPECT_GT(decoded_decimals[i], decoded_decimals[i-1]); |
250 | 13 | EXPECT_GT(decoded_decimals[i], test_decimals[i-1]); |
251 | 13 | EXPECT_GT(test_decimals[i], decoded_decimals[i-1]); |
252 | 13 | EXPECT_GT(test_decimals[i], test_decimals[i-1]); |
253 | | // This is not necessarily true for BigDecimal Serialization |
254 | | // EXPECT_TRUE(encoded_strings[i] > encoded_strings[i-1]); |
255 | 13 | } |
256 | 14 | } |
257 | | |
258 | | // Testing just outside the scale limits. |
259 | 1 | Decimal("-9847.236780e+2147483654").EncodeToSerializedBigDecimal(&is_out_of_range); |
260 | 1 | EXPECT_TRUE(is_out_of_range); |
261 | 1 | Decimal("-1.36e-2147483646").EncodeToSerializedBigDecimal(&is_out_of_range); |
262 | 1 | EXPECT_TRUE(is_out_of_range); |
263 | 1 | } |
264 | | |
265 | 1 | TEST_F(DecimalTest, TestFloatDoubleCanonicalization) { |
266 | 1 | const float float_nan_0 = CreateFloat(1, 0b11111111, (1 << 22)); |
267 | 1 | const float float_nan_1 = CreateFloat(0, 0b11111111, 1); |
268 | 1 | const float float_not_nan_0 = CreateFloat(0, 0b11111110, 1); |
269 | 1 | const float float_not_nan_1 = CreateFloat(0, 0b11111111, 0); |
270 | | |
271 | 1 | const double double_nan_0 = CreateDouble(1, 0b11111111111, (1l << 51)); |
272 | 1 | const double double_nan_1 = CreateDouble(0, 0b11111111111, 1); |
273 | 1 | const double double_not_nan_0 = CreateDouble(0, 0b11111111110, 1); |
274 | 1 | const double double_not_nan_1 = CreateDouble(0, 0b11111111111, 0); |
275 | | |
276 | 1 | EXPECT_TRUE(IsNanFloat(float_nan_0)); |
277 | 1 | EXPECT_TRUE(IsNanFloat(float_nan_1)); |
278 | 1 | EXPECT_FALSE(IsNanFloat(float_not_nan_0)); |
279 | 1 | EXPECT_FALSE(IsNanFloat(float_not_nan_1)); |
280 | | |
281 | 1 | EXPECT_TRUE(IsNanDouble(double_nan_0)); |
282 | 1 | EXPECT_TRUE(IsNanDouble(double_nan_1)); |
283 | 1 | EXPECT_FALSE(IsNanDouble(double_not_nan_0)); |
284 | 1 | EXPECT_FALSE(IsNanDouble(double_not_nan_1)); |
285 | | |
286 | 1 | float f1 = CanonicalizeFloat(float_nan_0); |
287 | 1 | float f2 = CanonicalizeFloat(float_nan_1); |
288 | 1 | EXPECT_EQ(*(reinterpret_cast<int32_t *>(&f1)), *(reinterpret_cast<int32_t *>(&f2))); |
289 | | |
290 | 1 | double d1 = CanonicalizeDouble(double_nan_0); |
291 | 1 | double d2 = CanonicalizeDouble(double_nan_1); |
292 | 1 | EXPECT_EQ(*(reinterpret_cast<int64_t *>(&d1)), *(reinterpret_cast<int64_t *>(&d2))); |
293 | 1 | } |
294 | | |
295 | | } // namespace util |
296 | | } // namespace yb |