blob: b9ff54b9d124e23160a9e7be50e3631eee1ae1bb [file]
/*
* 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 "big_decimal.h"
#include <cstring>
namespace ignite {
void big_decimal::set_scale(std::int16_t new_scale, big_decimal &res) const {
if (m_scale == new_scale)
return;
auto diff = std::int16_t(m_scale - new_scale);
big_integer adjustment;
if (diff > 0) {
big_integer::get_power_of_ten(diff, adjustment);
m_magnitude.divide(adjustment, res.m_magnitude);
} else {
big_integer::get_power_of_ten(-diff, adjustment);
m_magnitude.multiply(adjustment, res.m_magnitude);
}
res.m_scale = new_scale;
}
int big_decimal::compare(const big_decimal &other) const {
if (is_zero() && other.is_zero())
return 0;
if (m_scale == other.m_scale)
return m_magnitude.compare(other.m_magnitude);
else if (m_scale > other.m_scale) {
big_decimal scaled;
other.set_scale(m_scale, scaled);
return m_magnitude.compare(scaled.m_magnitude);
} else {
big_decimal scaled;
set_scale(other.m_scale, scaled);
return scaled.m_magnitude.compare(other.m_magnitude);
}
}
std::ostream &operator<<(std::ostream &os, const big_decimal &val) {
const big_integer &unscaled = val.get_unscaled_value();
// Zero magnitude case. Scale does not matter.
if (unscaled.get_magnitude().empty())
return os << '0';
// Scale is zero or negative. No decimal point here.
if (val.m_scale <= 0) {
os << unscaled;
// Adding zeroes if needed.
for (int32_t i = 0; i < -val.m_scale; ++i)
os << '0';
return os;
}
// Getting magnitude as a string.
std::stringstream converter;
converter << unscaled;
std::string magStr = converter.str();
auto magLen = int32_t(magStr.size());
int32_t magBegin = 0;
// If value is negative passing minus sign.
if (magStr[magBegin] == '-') {
os << magStr[magBegin];
++magBegin;
--magLen;
}
// Finding last non-zero char. There is no sense in trailing zeroes
// beyond the decimal point.
int32_t lastNonZero = static_cast<int32_t>(magStr.size()) - 1;
while (lastNonZero >= magBegin && magStr[lastNonZero] == '0')
--lastNonZero;
// This is expected as we already covered zero number case.
assert(lastNonZero >= magBegin);
int32_t dotPos = magLen - val.m_scale;
if (dotPos <= 0) {
// Means we need to add leading zeroes.
os << '0' << '.';
while (dotPos < 0) {
++dotPos;
os << '0';
}
os.write(&magStr[magBegin], lastNonZero - magBegin + 1);
} else {
// Decimal point is in the middle of the number.
// Just output everything before the decimal point.
os.write(&magStr[magBegin], dotPos);
int32_t afterDot = lastNonZero - dotPos - magBegin + 1;
if (afterDot > 0) {
os << '.';
os.write(&magStr[magBegin + dotPos], afterDot);
}
}
return os;
}
std::istream &operator>>(std::istream &is, big_decimal &val) {
std::istream::sentry sentry(is);
// Return zero if input failed.
val.assign_int64(0);
if (!is)
return is;
// Current char.
int c = is.peek();
// Current value parts.
uint64_t part = 0;
int32_t partDigits = 0;
int32_t scale = -1;
int32_t sign = 1;
big_integer &mag = val.m_magnitude;
big_integer pow;
big_integer bigPart;
if (!is)
return is;
// Checking sign.
if (c == '-' || c == '+') {
if (c == '-')
sign = -1;
is.ignore();
c = is.peek();
}
// Reading number itself.
while (is) {
if (isdigit(c)) {
part = part * 10 + (c - '0');
++partDigits;
} else if (c == '.' && scale < 0) {
// We have found decimal point. Starting counting scale.
scale = 0;
} else
break;
is.ignore();
c = is.peek();
if (part >= 1000000000000000000U) {
big_integer::get_power_of_ten(partDigits, pow);
mag.multiply(pow, mag);
mag.add(part);
part = 0;
partDigits = 0;
}
// Counting scale if the decimal point have been encountered.
if (scale >= 0)
++scale;
}
// Adding last part of the number.
if (partDigits) {
big_integer::get_power_of_ten(partDigits, pow);
mag.multiply(pow, mag);
mag.add(part);
}
// Adjusting scale.
if (scale < 0)
scale = 0;
else
--scale;
// Reading exponent.
if (c == 'e' || c == 'E') {
is.ignore();
int32_t exp = 0;
is >> exp;
scale -= exp;
}
val.m_scale = scale;
if (sign < 0)
mag.negate();
return is;
}
} // namespace ignite