/Users/deen/code/yugabyte-db/src/yb/util/decimal.cc
Line | Count | Source (jump to first uncovered line) |
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 | | #include "yb/util/decimal.h" |
14 | | |
15 | | #include <iomanip> |
16 | | #include <limits> |
17 | | #include <vector> |
18 | | |
19 | | #include "yb/gutil/casts.h" |
20 | | |
21 | | #include "yb/util/status_format.h" |
22 | | #include "yb/util/status_log.h" |
23 | | #include "yb/util/stol_utils.h" |
24 | | |
25 | | using std::string; |
26 | | using std::vector; |
27 | | |
28 | | namespace yb { |
29 | | namespace util { |
30 | | |
31 | 4.27k | Decimal::Decimal(const std::string& string_val) { |
32 | 4.27k | CHECK_OK(FromString(string_val)); |
33 | 4.27k | } |
34 | | |
35 | 5 | Decimal::Decimal(double double_val) { |
36 | 5 | CHECK_OK(FromDouble(double_val)); |
37 | 5 | } |
38 | | |
39 | 0 | Decimal::Decimal(const VarInt& varint_val) { |
40 | 0 | CHECK_OK(FromVarInt(varint_val)); |
41 | 0 | } |
42 | | |
43 | 93 | void Decimal::clear() { |
44 | 93 | digits_ = {}; |
45 | 93 | exponent_ = VarInt(0); |
46 | 93 | is_positive_ = true; |
47 | 93 | } |
48 | | |
49 | 18 | string Decimal::ToDebugString() const { |
50 | 18 | string output = "[ "; |
51 | 15 | output += is_positive_ ? "+" : "-"; |
52 | 18 | output += " 10^"; |
53 | 13 | output += exponent_.Sign() >= 0 ? "+" : ""; |
54 | 18 | output += exponent_.ToString() + " * 0."; |
55 | 37 | for (int digit : digits_) { |
56 | 37 | output += '0' + digit; |
57 | 37 | } |
58 | 18 | output += " ]"; |
59 | 18 | return output; |
60 | 18 | } |
61 | | |
62 | 8.19k | Status Decimal::ToPointString(string* string_val, const int max_length) const { |
63 | 8.19k | if (digits_.empty()) { |
64 | 4 | *string_val = "0"; |
65 | 4 | return Status::OK(); |
66 | 4 | } |
67 | 8.18k | int64_t exponent = VERIFY_RESULT(exponent_.ToInt64()); |
68 | 8.18k | if (exponent > max_length || exponent < -max_length) { |
69 | 9 | return STATUS_SUBSTITUTE(InvalidArgument, |
70 | 9 | "Max length $0 too small to encode decimal with exponent $1", max_length, exponent); |
71 | 9 | } |
72 | 8.17k | string output; |
73 | 8.17k | if (!is_positive_) { |
74 | 11 | output = "-"; |
75 | 11 | } |
76 | 8.17k | if (exponent <= 0) { |
77 | 12 | output += "0."; |
78 | 46 | for (int i = 0; i < -exponent; i++) { |
79 | 34 | output.push_back('0'); |
80 | 34 | } |
81 | 170 | for (size_t i = 0; i < digits_.size(); i++) { |
82 | 168 | output.push_back('0' + digits_[i]); |
83 | 168 | if (implicit_cast<int64_t>(output.size()) > max_length) { |
84 | 10 | return STATUS_SUBSTITUTE(InvalidArgument, |
85 | 10 | "Max length $0 too small to encode Decimal", max_length); |
86 | 10 | } |
87 | 168 | } |
88 | 8.16k | } else { |
89 | 16.5k | for (size_t i = 0; i < digits_.size(); i++) { |
90 | 8.38k | if (implicit_cast<size_t>(exponent) == i) { |
91 | 39 | output.push_back('.'); |
92 | 39 | } |
93 | 8.38k | output.push_back('0' + digits_[i]); |
94 | 8.38k | if (implicit_cast<int64_t>(output.size()) > max_length) { |
95 | 4 | return STATUS_SUBSTITUTE(InvalidArgument, |
96 | 4 | "Max length $0 too small to encode Decimal", max_length); |
97 | 4 | } |
98 | 8.38k | } |
99 | 24.3k | for (ssize_t i = digits_.size(); i < exponent; i++) { |
100 | 16.2k | output.push_back('0'); |
101 | 16.2k | if (implicit_cast<int64_t>(output.size()) > max_length) { |
102 | 0 | return STATUS_SUBSTITUTE(InvalidArgument, |
103 | 0 | "Max length $0 too small to encode Decimal", max_length); |
104 | 0 | } |
105 | 16.2k | } |
106 | 8.15k | } |
107 | 8.16k | *string_val = std::move(output); |
108 | 8.16k | return Status::OK(); |
109 | 8.17k | } |
110 | | |
111 | 31 | string Decimal::ToScientificString() const { |
112 | 31 | if (digits_.empty()) { |
113 | 1 | return "0"; |
114 | 1 | } |
115 | 30 | string output; |
116 | 30 | if (!is_positive_) { |
117 | 4 | output = "-"; |
118 | 4 | } |
119 | 30 | output.push_back('0' + digits_[0]); |
120 | 30 | output.push_back('.'); |
121 | 732 | for (size_t i = 1; i < digits_.size(); i++) { |
122 | 702 | output.push_back('0' + digits_[i]); |
123 | 702 | } |
124 | 30 | output.push_back('e'); |
125 | 30 | VarInt exponent = exponent_ + VarInt(-1); |
126 | 30 | if (exponent.Sign() >= 0) { |
127 | 12 | output += "+"; |
128 | 12 | } |
129 | 30 | output += exponent.ToString(); |
130 | 30 | return output; |
131 | 30 | } |
132 | | |
133 | 8.18k | string Decimal::ToString() const { |
134 | 8.18k | string output; |
135 | 8.18k | if (Decimal::ToPointString(&output).ok()) { |
136 | 8.15k | return output; |
137 | 25 | } else { |
138 | 25 | return ToScientificString(); |
139 | 25 | } |
140 | 8.18k | } |
141 | | |
142 | 13 | Result<long double> Decimal::ToDouble() const { |
143 | 13 | return CheckedStold(ToString()); |
144 | 13 | } |
145 | | |
146 | 2 | Result<VarInt> Decimal::ToVarInt() const { |
147 | 2 | string string_val; |
148 | 2 | RETURN_NOT_OK(ToPointString(&string_val, kUnlimitedMaxLength)); |
149 | | |
150 | 2 | if (!is_integer()) { |
151 | 0 | return STATUS_SUBSTITUTE(InvalidArgument, |
152 | 0 | "Cannot convert non-integer Decimal into integer: $0", string_val); |
153 | 0 | } |
154 | | |
155 | 2 | return VarInt::CreateFromString(string_val); |
156 | 2 | } |
157 | | |
158 | 7.05k | Status Decimal::FromString(const Slice &slice) { |
159 | 7.05k | if (slice.empty()) { |
160 | 1 | return STATUS_SUBSTITUTE(InvalidArgument, |
161 | 1 | "Cannot decode empty slice to Decimal: $0", slice.ToString()); |
162 | 1 | } |
163 | 7.05k | is_positive_ = slice[0] != '-'; |
164 | 7.05k | size_t i = 0; |
165 | 7.05k | if (slice[0] == '+' || slice[0] == '-') { |
166 | 1.08k | i++; |
167 | 1.08k | } |
168 | 7.05k | bool point_found = false; |
169 | 7.05k | size_t point_position = 0; |
170 | 7.05k | digits_.clear(); |
171 | 320k | for (; i < slice.size() && slice[i] != 'e' && slice[i] != 'E'; i++) { |
172 | 313k | if (slice[i] == '.' && !point_found) { |
173 | 2.69k | point_found = true; |
174 | 2.69k | point_position = digits_.size(); |
175 | 2.69k | continue; |
176 | 2.69k | } |
177 | 310k | if (PREDICT_TRUE(slice[i] >= '0' && slice[i] <= '9')) { |
178 | 310k | digits_.push_back(slice[i]-'0'); |
179 | 2 | } else { |
180 | 2 | return STATUS_SUBSTITUTE( |
181 | 2 | InvalidArgument, |
182 | 2 | "Invalid character $0 found at position $1 when parsing Decimal $2", |
183 | 2 | slice[i], i, slice.ToString()); |
184 | 2 | } |
185 | 310k | } |
186 | 7.04k | if (PREDICT_FALSE(digits_.empty())) { |
187 | 1 | return STATUS_SUBSTITUTE( |
188 | 1 | InvalidArgument, |
189 | 1 | "There are no digits in the decimal $0 before the e / E", |
190 | 1 | slice.ToBuffer()); |
191 | 1 | } |
192 | 7.04k | if (!point_found) { |
193 | 4.35k | point_position = digits_.size(); |
194 | 4.35k | } |
195 | 7.04k | if (i < slice.size()) { |
196 | 833 | Slice exponent_slice(slice); |
197 | 833 | exponent_slice.remove_prefix(i+1); |
198 | 833 | RETURN_NOT_OK(exponent_.FromString(exponent_slice.ToBuffer())); |
199 | 6.21k | } else { |
200 | 6.21k | exponent_ = VarInt(0); |
201 | 6.21k | } |
202 | 7.04k | exponent_ = exponent_ + VarInt(static_cast<int64_t> (point_position)); |
203 | 7.04k | make_canonical(); |
204 | 7.04k | return Status::OK(); |
205 | 7.04k | } |
206 | | |
207 | | constexpr size_t kPrecisionLimit = 20; |
208 | | |
209 | | namespace { |
210 | | |
211 | 13 | string StringFromDouble(double double_val, int precision_limit = kPrecisionLimit) { |
212 | 13 | std::stringstream ss; |
213 | 13 | ss << std::setprecision(precision_limit); |
214 | 13 | ss << double_val; |
215 | 13 | return ss.str(); |
216 | 13 | } |
217 | | |
218 | | } // namespace |
219 | | |
220 | 13 | Status Decimal::FromDouble(double double_val) { |
221 | 13 | string str = StringFromDouble(double_val); |
222 | 13 | if (str == "nan") { |
223 | 2 | return STATUS(Corruption, "Cannot convert nan to Decimal"); |
224 | 11 | } else if (str == "-nan") { |
225 | 0 | return STATUS(Corruption, "Cannot convert -nan to Decimal"); |
226 | 11 | } else if (str == "inf") { |
227 | 1 | return STATUS(Corruption, "Cannot convert inf to Decimal"); |
228 | 10 | } else if (str == "-inf") { |
229 | 1 | return STATUS(Corruption, "Cannot convert -inf to Decimal"); |
230 | 1 | } |
231 | 9 | return FromString(str); |
232 | 9 | } |
233 | | |
234 | 0 | Status Decimal::FromVarInt(const VarInt &varint_val) { |
235 | 0 | return FromString(varint_val.ToString()); |
236 | 0 | } |
237 | | |
238 | 18 | bool Decimal::is_integer() const { |
239 | 18 | return digits_.empty() || exponent_ >= VarInt(static_cast<int64_t>(digits_.size())); |
240 | 18 | } |
241 | | |
242 | 132 | int Decimal::CompareTo(const Decimal &other) const { |
243 | 132 | if (is_positive_ != other.is_positive_) { |
244 | 8 | return static_cast<int>(is_positive_) - static_cast<int>(other.is_positive_); |
245 | 8 | } |
246 | 124 | int comp; |
247 | | // We must compare zeros in a special way because the exponent logic doesn't work with special |
248 | | // canonicalization for zero. |
249 | 124 | if (digits_.empty() || other.digits_.empty()) { |
250 | 10 | comp = static_cast<int>(digits_.empty()) - static_cast<int>(other.digits_.empty()); |
251 | 10 | return is_positive_ ? -comp : comp; |
252 | 10 | } |
253 | 114 | comp = exponent_.CompareTo(other.exponent_); |
254 | 114 | if (comp != 0) { |
255 | 32 | return is_positive_ ? comp : -comp; |
256 | 56 | } |
257 | 232 | for (size_t i = 0; i < digits_.size() && i < other.digits_.size(); i++) { |
258 | 198 | comp = static_cast<int>(digits_[i]) - static_cast<int>(other.digits_[i]); |
259 | 198 | if (comp != 0) { |
260 | 16 | return is_positive_ ? comp : -comp; |
261 | 24 | } |
262 | 198 | } |
263 | 34 | comp = static_cast<int>(this->digits_.size()) - static_cast<int>(other.digits_.size()); |
264 | 22 | return is_positive_ ? comp : -comp; |
265 | 58 | } |
266 | | |
267 | | // Encodes pairs of digits into one byte each. The last bit in each byte is the |
268 | | // "continuation bit" which is equal to 1 for all bytes except the last. |
269 | 115k | std::string EncodeToDigitPairs(const std::vector<uint8_t>& digits) { |
270 | 115k | if (digits.empty()) { |
271 | 0 | return std::string(); |
272 | 0 | } |
273 | 115k | size_t len = (digits.size() + 1) / 2; |
274 | 115k | std::string result(len, 0); |
275 | 4.10M | for (size_t i = 0; i < len - 1; ++i) { |
276 | 3.99M | result[i] = (digits[i * 2] * 10 + digits[i * 2 + 1]) * 2 + 1; |
277 | 3.99M | } |
278 | 115k | size_t i = len - 1; |
279 | 115k | uint8_t last_byte = digits[i * 2] * 10; |
280 | 115k | if (i * 2 + 1 < digits.size()) { |
281 | 71.8k | last_byte += digits[i * 2 + 1]; |
282 | 71.8k | } |
283 | 115k | result[i] = last_byte * 2; |
284 | 115k | return result; |
285 | 115k | } |
286 | | |
287 | 115k | string Decimal::EncodeToComparable() const { |
288 | | // Zero is encoded to the special value 128. |
289 | 115k | if (digits_.empty()) { |
290 | 57 | return string(1, static_cast<char>(128)); |
291 | 57 | } |
292 | | // We reserve two bits for sign: -, zero, and +. Their sign portions are resp. '00', '10', '11'. |
293 | 115k | string exponent = exponent_.EncodeToComparable(/* num_reserved_bits */ 2); |
294 | 115k | const string mantissa = EncodeToDigitPairs(digits_); |
295 | 115k | string output = exponent + mantissa; |
296 | | // The first two (reserved) bits are set to 1 here. |
297 | 115k | output[0] |= 0xc0; |
298 | | // For negatives, everything is complemented (including the sign bits) which were set to 1 above. |
299 | 115k | if (!is_positive_) { |
300 | 2.22M | for (size_t i = 0; i < output.size(); i++) { |
301 | 2.19M | output[i] = ~output[i]; // Bitwise not. |
302 | 2.19M | } |
303 | 32.3k | } |
304 | 115k | return output; |
305 | 115k | } |
306 | | |
307 | | CHECKED_STATUS DecodeFromDigitPairs( |
308 | 194k | const Slice& slice, size_t *num_decoded_bytes, std::vector<uint8_t>* digits) { |
309 | 194k | digits->clear(); |
310 | 194k | digits->reserve(slice.size() * 2); |
311 | 194k | *num_decoded_bytes = 0; |
312 | 7.40M | for (size_t i = 0; i < slice.size(); i++) { |
313 | 7.20M | uint8_t byte = slice[i]; |
314 | 7.20M | if (!(byte & 1)) { |
315 | 194k | *num_decoded_bytes = i + 1; |
316 | 194k | i = slice.size(); |
317 | 194k | } |
318 | 7.20M | byte /= 2; |
319 | 7.20M | digits->push_back(byte / 10); |
320 | 7.20M | digits->push_back(byte % 10); |
321 | 7.20M | } |
322 | 194k | if (*num_decoded_bytes == 0) { |
323 | 0 | return STATUS(Corruption, "Decoded the whole slice but didn't find the ending"); |
324 | 0 | } |
325 | 269k | while (!digits->empty() && !digits->back()) { |
326 | 75.1k | digits->pop_back(); |
327 | 75.1k | } |
328 | 194k | return Status::OK(); |
329 | 194k | } |
330 | | |
331 | 194k | Status Decimal::DecodeFromComparable(const Slice& slice, size_t *num_decoded_bytes) { |
332 | 194k | if (slice.empty()) { |
333 | 0 | return STATUS(Corruption, "Cannot decode Decimal from empty slice."); |
334 | 0 | } |
335 | | // Zero is specially decoded from the value 128. |
336 | 194k | if (slice[0] == 128) { |
337 | 71 | clear(); |
338 | 71 | *num_decoded_bytes = 1; |
339 | 71 | return Status::OK(); |
340 | 71 | } |
341 | | // The first bit is enough to decode the sign. |
342 | 194k | is_positive_ = slice[0] >= 128; |
343 | | // We have to complement everything if negative, so we are making a copy. |
344 | 194k | string encoded = slice.ToBuffer(); |
345 | 194k | if (!is_positive_) { |
346 | 4.27M | for (size_t i = 0; i < encoded.size(); i++) { |
347 | 4.21M | encoded[i] = ~encoded[i]; |
348 | 4.21M | } |
349 | 57.5k | } |
350 | 194k | size_t num_exponent_bytes = 0; |
351 | 194k | RETURN_NOT_OK(exponent_.DecodeFromComparable( |
352 | 194k | encoded, &num_exponent_bytes, /* num_reserved_bits */ 2)); |
353 | 194k | Slice remaining_slice(encoded); |
354 | 194k | remaining_slice.remove_prefix(num_exponent_bytes); |
355 | 194k | size_t num_mantissa_bytes = 0; |
356 | 194k | RETURN_NOT_OK(DecodeFromDigitPairs(remaining_slice, &num_mantissa_bytes, &digits_)); |
357 | 194k | *num_decoded_bytes = num_exponent_bytes + num_mantissa_bytes; |
358 | 194k | return Status::OK(); |
359 | 194k | } |
360 | | |
361 | 19.7k | Status Decimal::DecodeFromComparable(const Slice& slice) { |
362 | 19.7k | size_t num_decoded_bytes; |
363 | 19.7k | return DecodeFromComparable(slice, &num_decoded_bytes); |
364 | 19.7k | } |
365 | | |
366 | 11.3k | string Decimal::EncodeToSerializedBigDecimal(bool* is_out_of_range) const { |
367 | | // Note that BigDecimal's scale is not the same as our exponent, but related by the following: |
368 | 11.3k | VarInt varint_scale = VarInt(static_cast<int64_t>(digits_.size())) - exponent_; |
369 | | // Must use 4 bytes for the two's complement encoding of the scale. |
370 | 11.3k | string scale = varint_scale.EncodeToTwosComplement(); |
371 | 11.3k | if (scale.length() > 4) { |
372 | 2 | *is_out_of_range = true; |
373 | 2 | return std::string(); |
374 | 2 | } |
375 | 11.3k | *is_out_of_range = false; |
376 | 11.3k | if (scale.length() < 4) { |
377 | 8.45k | scale = std::string(4 - scale.length(), varint_scale.Sign() < 0 ? 0xff : 0x00) + scale; |
378 | 11.3k | } |
379 | | |
380 | 11.3k | std::vector<char> digits; |
381 | 11.3k | digits.reserve(digits_.size() + 1); |
382 | 1.52M | for (auto digit : digits_) { |
383 | 1.52M | digits.push_back('0' + digit); |
384 | 1.52M | } |
385 | 11.3k | digits.push_back(0); |
386 | | |
387 | | // Note that the mantissa varint needs to have the same sign as the current decimal. |
388 | | // Get the digit array in int from int8_t in this class. |
389 | 11.3k | auto temp = !digits_.empty() ? CHECK_RESULT(VarInt::CreateFromString(digits.data())) : VarInt(0); |
390 | 11.3k | if (!is_positive_) { |
391 | 6.36k | temp.Negate(); |
392 | 6.36k | } |
393 | 11.3k | string mantissa = temp.EncodeToTwosComplement(); |
394 | 11.3k | return scale + mantissa; |
395 | 11.3k | } |
396 | | |
397 | 219 | Status Decimal::DecodeFromSerializedBigDecimal(Slice slice) { |
398 | 219 | if (slice.size() < 5) { |
399 | 0 | return STATUS_SUBSTITUTE( |
400 | 0 | Corruption, "Serialized BigDecimal must have at least 5 bytes. Found $0", slice.size()); |
401 | 0 | } |
402 | | // Decode the scale from the first 4 bytes. |
403 | 219 | VarInt scale; |
404 | 219 | RETURN_NOT_OK(scale.DecodeFromTwosComplement(std::string(slice.cdata(), 4))); |
405 | 219 | slice.remove_prefix(4); |
406 | 219 | VarInt mantissa; |
407 | 219 | RETURN_NOT_OK(mantissa.DecodeFromTwosComplement(slice.ToBuffer())); |
408 | 219 | bool negative = mantissa.Sign() < 0; |
409 | 219 | if (negative) { |
410 | 54 | mantissa.Negate(); |
411 | 54 | } |
412 | 219 | auto mantissa_str = mantissa.ToString(); |
413 | | // The sign of the BigDecimal is the sign of the mantissa |
414 | 219 | is_positive_ = !negative; |
415 | 219 | digits_.resize(mantissa_str.size()); |
416 | 28.8k | for (size_t i = 0; i != mantissa_str.size(); ++i) { |
417 | 28.6k | digits_[i] = mantissa_str[i] - '0'; |
418 | 28.6k | } |
419 | 219 | exponent_ = VarInt(static_cast<int64_t> (digits_.size())) - scale; |
420 | 219 | make_canonical(); |
421 | 219 | return Status::OK(); |
422 | 219 | } |
423 | | |
424 | 0 | bool Decimal::IsIdenticalTo(const Decimal &other) const { |
425 | 0 | return |
426 | 0 | is_positive_ == other.is_positive_ && |
427 | 0 | exponent_ == other.exponent_ && |
428 | 0 | digits_ == other.digits_; |
429 | 0 | } |
430 | | |
431 | 7.80k | bool Decimal::is_canonical() const { |
432 | 7.80k | if (digits_.empty()) { |
433 | 7 | return is_positive_ && exponent_ == VarInt(0); |
434 | 7 | } |
435 | 7.79k | return digits_.front() != 0 && digits_.back() != 0; |
436 | 7.79k | } |
437 | | |
438 | 7.80k | void Decimal::make_canonical() { |
439 | 7.80k | if (is_canonical()) { |
440 | 4.20k | return; |
441 | 4.20k | } |
442 | 3.59k | size_t num_zeros = 0; |
443 | 3.80k | while (num_zeros < digits_.size() && digits_[num_zeros] == 0) { |
444 | 205 | num_zeros++; |
445 | 205 | } |
446 | 3.59k | exponent_ = exponent_ - VarInt(num_zeros); |
447 | 87.7k | for (size_t i = num_zeros; i < digits_.size() + num_zeros; i++) { |
448 | 83.9k | digits_[i-num_zeros] = i < digits_.size() ? digits_[i] : 0; |
449 | 84.1k | } |
450 | 8.74k | while (!digits_.empty() && digits_.back() == 0) { |
451 | 5.14k | digits_.pop_back(); |
452 | 5.14k | } |
453 | 3.59k | if (digits_.empty()) { |
454 | 22 | clear(); |
455 | 22 | } |
456 | 3.59k | } |
457 | | |
458 | 11.3k | Decimal DecimalFromComparable(const Slice& slice) { |
459 | 11.3k | Decimal decimal; |
460 | 11.3k | CHECK_OK(decimal.DecodeFromComparable(slice)); |
461 | 11.3k | return decimal; |
462 | 11.3k | } |
463 | | |
464 | 11.3k | Decimal DecimalFromComparable(const std::string& str) { |
465 | 11.3k | return DecimalFromComparable(Slice(str)); |
466 | 11.3k | } |
467 | | |
468 | | // Normalize so that both Decimals have same exponent and same number of digits |
469 | | // Add digits considering sign |
470 | | // Canonicalize result |
471 | | |
472 | 115 | Decimal Decimal::operator+(const Decimal& other) const { |
473 | 115 | Decimal decimal(digits_, exponent_, is_positive_); |
474 | 115 | Decimal other1(other.digits_, other.exponent_, other.is_positive_); |
475 | | |
476 | | // Normalize the exponents |
477 | | // eg. if we are adding 0.1E+3 and 0.5E+2, we first convert them to 0.1E+3 and 0.05E+3 |
478 | 115 | VarInt max_exponent = std::max(other1.exponent_, decimal.exponent_); |
479 | 115 | VarInt var_int_one(1); |
480 | 115 | if (decimal.exponent_ < max_exponent) { |
481 | 3 | VarInt increase_varint = max_exponent - decimal.exponent_; |
482 | 3 | int64_t increase = CHECK_RESULT(increase_varint.ToInt64()); |
483 | 3 | decimal.digits_.insert(decimal.digits_.begin(), increase, 0); |
484 | 3 | decimal.exponent_ = max_exponent; |
485 | 3 | } |
486 | 115 | if (other1.exponent_ < max_exponent) { |
487 | 111 | VarInt increase_varint = max_exponent - other1.exponent_; |
488 | 111 | int64_t increase = CHECK_RESULT(increase_varint.ToInt64()); |
489 | 111 | other1.digits_.insert(other1.digits_.begin(), increase, 0); |
490 | 111 | other1.exponent_ = max_exponent; |
491 | 111 | } |
492 | | |
493 | | // Make length of digits the same. |
494 | | // If we need to add 0.1E+3 and 0.05E+3, we convert them to 0.10E+3 and 0.05E+3 |
495 | 115 | size_t max_digits = std::max(decimal.digits_.size(), other1.digits_.size()); |
496 | 115 | if (decimal.digits_.size() < max_digits) { |
497 | 29 | auto increase = max_digits - decimal.digits_.size(); |
498 | 1.75k | for (size_t i = 0; i < increase; i = i + 1) { |
499 | 1.72k | decimal.digits_.push_back(0); |
500 | 1.72k | } |
501 | 29 | } |
502 | 115 | if (other1.digits_.size() < max_digits) { |
503 | 85 | auto increase = max_digits - other1.digits_.size(); |
504 | 41.9k | for (size_t i = 0; i < increase; i++) { |
505 | 41.8k | other1.digits_.push_back(0); |
506 | 41.8k | } |
507 | 85 | } |
508 | | |
509 | | // Add mantissa |
510 | | // For the case when the both numbers are positive (eg. 0.1E+3 and 0.05E+3) |
511 | | // or both negative (eg. -0.1E+3 and -0.05E+3), we just add the mantissas |
512 | 115 | if (decimal.is_positive_ == other1.is_positive_) { |
513 | 52.4k | for (int64_t i = max_digits - 1; i >= 0; i--) { |
514 | 52.3k | decimal.digits_[i] += other1.digits_[i]; |
515 | 52.3k | if (decimal.digits_[i] > 9) { |
516 | 2.80k | decimal.digits_[i] -= 10; |
517 | 2.80k | if (i > 0) { |
518 | 2.80k | decimal.digits_[i - 1]++; |
519 | 0 | } else { |
520 | 0 | decimal.digits_.insert(decimal.digits_.begin(), 1); |
521 | 0 | decimal.exponent_ = decimal.exponent_ + var_int_one; |
522 | 0 | } |
523 | 2.80k | } |
524 | 52.3k | } |
525 | 51 | } else { |
526 | | // For the case when the two numbers have opposite sign (eg. 0.1E+3 and -0.05E+3) |
527 | | // or (-0.1E+3 and 0.05E+3), we subtract the smaller mantissa from the larger mantissa |
528 | | // and use the sign of the larger mantissa |
529 | 51 | int comp = 0; // indicates whether mantissa of this is bigger |
530 | 51 | for (size_t i = 0; i < decimal.digits_.size() && i < other1.digits_.size(); i++) { |
531 | 51 | comp = static_cast<int>(decimal.digits_[i]) - static_cast<int>(other1.digits_[i]); |
532 | 51 | if (comp != 0) { |
533 | 51 | break; |
534 | 51 | } |
535 | 51 | } |
536 | 51 | if (comp < 0) { |
537 | 2 | decimal.digits_.swap(other1.digits_); |
538 | 2 | decimal.is_positive_ = !decimal.is_positive_; |
539 | 2 | } |
540 | 82.7k | for (int64_t i = max_digits - 1; i >= 0; i--) { |
541 | 82.6k | if (decimal.digits_[i] >= other1.digits_[i]) { |
542 | 78.2k | decimal.digits_[i] -= other1.digits_[i]; |
543 | 4.46k | } else { |
544 | 4.46k | other1.digits_[i-1]++; |
545 | 4.46k | decimal.digits_[i] = decimal.digits_[i] + 10 - other1.digits_[i]; |
546 | 4.46k | } |
547 | 82.6k | } |
548 | 51 | } |
549 | | |
550 | | // Finally we normalize the number obtained to get rid of leading and trailing 0's |
551 | | // in the mantissa. |
552 | 115 | decimal.make_canonical(); |
553 | 115 | return decimal; |
554 | 115 | } |
555 | | |
556 | 0 | std::ostream& operator<<(ostream& os, const Decimal& d) { |
557 | 0 | os << d.ToString(); |
558 | 0 | return os; |
559 | 0 | } |
560 | | |
561 | | } // namespace util |
562 | | } // namespace yb |