22#ifndef FBCPP_NUMERIC_CONVERTER_H 
   23#define FBCPP_NUMERIC_CONVERTER_H 
   48    struct NumberTypePriority
 
   50        static constexpr int value = 0;
 
   53#if FB_CPP_USE_BOOST_MULTIPRECISION != 0 
   57        static constexpr int value = 8;
 
   63        static constexpr int value = 7;
 
   68    struct NumberTypePriority<double>
 
   70        static constexpr int value = 6;
 
   74    struct NumberTypePriority<float>
 
   76        static constexpr int value = 5;
 
   79#if FB_CPP_USE_BOOST_MULTIPRECISION != 0 
   83        static constexpr int value = 4;
 
   88    struct NumberTypePriority<std::int64_t>
 
   90        static constexpr int value = 3;
 
   94    struct NumberTypePriority<std::int32_t>
 
   96        static constexpr int value = 2;
 
  100    struct NumberTypePriority<std::int16_t>
 
  102        static constexpr int value = 1;
 
  105    template <
typename T>
 
  106    inline constexpr int NumberTypePriorityValue =
 
  107        NumberTypePriority<std::remove_cv_t<std::remove_reference_t<T>>>::value;
 
  109    template <
typename T1, 
typename T2>
 
  110    using GreaterNumberType = std::conditional_t<(NumberTypePriorityValue<T1> >= NumberTypePriorityValue<T2>), T1, T2>;
 
  112    template <
typename T>
 
  113    inline constexpr bool IsFloatingNumber = std::is_floating_point_v<T>
 
  114#if FB_CPP_USE_BOOST_MULTIPRECISION != 0 
  115        || std::same_as<T, BoostDecFloat16> || std::same_as<T, BoostDecFloat34>
 
  119    template <
typename T>
 
  120    concept FloatingNumber = IsFloatingNumber<T>;
 
  122    template <
typename T>
 
  123    concept IntegralNumber = std::is_integral_v<T>
 
  124#if FB_CPP_USE_BOOST_MULTIPRECISION != 0 
  125        || std::same_as<T, BoostInt128>
 
  129    template <
typename T>
 
  132        using type = std::make_unsigned_t<T>;
 
  135#if FB_CPP_USE_BOOST_MULTIPRECISION != 0 
  139        using type = boost::multiprecision::uint128_t;
 
  143    template <
typename T>
 
  144    using MakeUnsignedType = 
typename MakeUnsigned<T>::type;
 
  146    class NumericConverter final
 
  149        explicit NumericConverter(Client& client, StatusWrapper* statusWrapper)
 
  151              statusWrapper{statusWrapper}
 
  155        [[noreturn]] 
void throwNumericOutOfRange()
 
  157            static constexpr std::intptr_t STATUS_NUMERIC_OUT_OF_RANGE[] = {
 
  159                isc_numeric_out_of_range,
 
  163            throw DatabaseException(client, STATUS_NUMERIC_OUT_OF_RANGE);
 
  166        [[noreturn]] 
void throwConversionErrorFromString(
const std::string& str)
 
  168            const std::intptr_t STATUS_CONVERSION_ERROR_FROM_STRING[] = {
 
  170                reinterpret_cast<std::intptr_t
>(str.c_str()),
 
  174            throw DatabaseException(client, STATUS_CONVERSION_ERROR_FROM_STRING);
 
  178        template <IntegralNumber To, IntegralNumber From>
 
  179        To numberToNumber(
const ScaledNumber<From>& from, 
int toScale)
 
  181            const int scaleDiff = toScale - from.scale;
 
  183            using ComputeType = GreaterNumberType<
decltype(from.value), To>;
 
  185            ComputeType result = 
static_cast<ComputeType
>(from.value);
 
  189                adjustScale(result, scaleDiff, 
static_cast<ComputeType
>(std::numeric_limits<To>::min()),
 
  190                    static_cast<ComputeType
>(std::numeric_limits<To>::max()));
 
  193            if (result < 
static_cast<ComputeType
>(std::numeric_limits<To>::min()) ||
 
  194                result > 
static_cast<ComputeType
>(std::numeric_limits<To>::max()))
 
  196                throwNumericOutOfRange();
 
  199            return static_cast<To
>(result);
 
  202        template <IntegralNumber To, FloatingNumber From>
 
  203        To numberToNumber(
const From& from, 
int toScale)
 
  205            using ComputeType = GreaterNumberType<double, From>;
 
  207            ComputeType value{from};
 
  208            const ComputeType eps = conversionEpsilon<ComputeType>();
 
  211                value /= powerOfTen(toScale);
 
  212            else if (toScale < 0)
 
  213                value *= powerOfTen(-toScale);
 
  220            static const auto minLimit = 
static_cast<ComputeType
>(std::numeric_limits<To>::min());
 
  221            static const auto maxLimit = 
static_cast<ComputeType
>(std::numeric_limits<To>::max());
 
  223            if (value < minLimit)
 
  225                if (value > minLimit - 1.0f)
 
  226                    return std::numeric_limits<To>::min();
 
  227                throwNumericOutOfRange();
 
  230            if (value > maxLimit)
 
  232                if (value < maxLimit + 1.0f)
 
  233                    return std::numeric_limits<To>::max();
 
  234                throwNumericOutOfRange();
 
  237            return static_cast<To
>(value);
 
  240        template <FloatingNumber To, 
typename From>
 
  241        To numberToNumber(
const ScaledNumber<From>& from, 
int toScale = 0)
 
  243            assert(toScale == 0);
 
  245            using ComputeType = GreaterNumberType<double, To>;
 
  247            ComputeType value = 
static_cast<ComputeType
>(from.value);  
 
  251                if (std::abs(from.scale) > std::numeric_limits<To>::max_exponent10)
 
  252                    throwNumericOutOfRange();
 
  255                    value *= powerOfTen(from.scale);
 
  256                else if (from.scale < 0)
 
  257                    value /= powerOfTen(-from.scale);
 
  260            return static_cast<To
>(value);
 
  263        template <FloatingNumber To, FloatingNumber From>
 
  264        To numberToNumber(
const From& from, 
int toScale = 0)
 
  266            assert(toScale == 0);
 
  268            if constexpr (std::is_floating_point_v<From> && !std::is_floating_point_v<To>)
 
  269                return To{std::format(
"{:.16e}", from)};
 
  271                return static_cast<To
>(from);
 
  274        template <IntegralNumber From>
 
  275        std::string numberToString(
const ScaledNumber<From>& from)
 
  279            const bool isNegative = from.value < 0;
 
  280            const bool isMinLimit = from.value == std::numeric_limits<
decltype(from.value)>::min();
 
  282            using UnsignedType = MakeUnsignedType<
decltype(from.value)>;
 
  284            auto unsignedValue = isMinLimit ? 
static_cast<UnsignedType
>(-(from.value + 1)) + 1
 
  285                                            : 
static_cast<UnsignedType
>(isNegative ? -from.value : from.value);
 
  291                buffer[digitCount++] = 
static_cast<char>((unsignedValue % 10) + 
'0');
 
  293            } 
while (unsignedValue > 0);
 
  302                for (
int i = digitCount - 1; i >= 0; --i)
 
  305                result.append(
static_cast<std::string::size_type
>(from.scale), 
'0');
 
  309                const int decimalPlaces = -from.scale;
 
  311                if (decimalPlaces >= 
static_cast<int>(digitCount))
 
  314                    const int leadingZeros = decimalPlaces - digitCount;
 
  315                    result.append(
static_cast<std::string::size_type
>(leadingZeros), 
'0');
 
  317                    for (
int i = digitCount - 1; i >= 0; --i)
 
  322                    for (
int i = digitCount - 1; i >= decimalPlaces; --i)
 
  327                    for (
int i = decimalPlaces - 1; i >= 0; --i)
 
  335        template <FloatingNumber From>
 
  336        std::string numberToString(
const From& from)
 
  338            if constexpr (std::is_floating_point_v<From>)
 
  339                return std::to_string(from);
 
  344        std::string opaqueInt128ToString(
const OpaqueInt128& opaqueInt128, 
int scale)
 
  346            const auto int128Util = client.getUtil()->getInt128(statusWrapper);
 
  347            char buffer[fb::IInt128::STRING_SIZE + 1];
 
  348            int128Util->toString(statusWrapper, &opaqueInt128, scale, 
static_cast<unsigned>(
sizeof(buffer)), buffer);
 
  352        std::string opaqueDecFloat16ToString(
const OpaqueDecFloat16& opaqueDecFloat16)
 
  354            const auto decFloat16Util = client.getDecFloat16Util(statusWrapper);
 
  355            char buffer[fb::IDecFloat16::STRING_SIZE + 1];
 
  356            decFloat16Util->toString(statusWrapper, &opaqueDecFloat16, 
static_cast<unsigned>(
sizeof(buffer)), buffer);
 
  360        std::string opaqueDecFloat34ToString(
const OpaqueDecFloat34& opaqueDecFloat34)
 
  362            const auto decFloat34Util = client.getDecFloat34Util(statusWrapper);
 
  363            char buffer[fb::IDecFloat34::STRING_SIZE + 1];
 
  364            decFloat34Util->toString(statusWrapper, &opaqueDecFloat34, 
static_cast<unsigned>(
sizeof(buffer)), buffer);
 
  368#if FB_CPP_USE_BOOST_MULTIPRECISION != 0 
  371            const boost::multiprecision::uint128_t boostUInt128{boostInt128};
 
  374            opaqueInt128.fb_data[0] = 
static_cast<std::uint64_t
>(boostUInt128 & 0xFFFFFFFFFFFFFFFFULL);
 
  375            opaqueInt128.fb_data[1] = 
static_cast<std::uint64_t
>(boostUInt128 >> 64);
 
  382            const auto high = 
static_cast<std::int64_t
>(opaqueInt128.fb_data[1]);
 
  385            boostInt128 += 
static_cast<BoostInt128>(opaqueInt128.fb_data[0]);
 
  391            const auto decFloat16Util = client.getDecFloat16Util(statusWrapper);
 
  393            decFloat16Util->fromString(statusWrapper, boostDecFloat16.str().c_str(), &opaqueDecFloat16);
 
  394            return opaqueDecFloat16;
 
  404            const auto decFloat34Util = client.getDecFloat34Util(statusWrapper);
 
  406            decFloat34Util->fromString(statusWrapper, boostDecFloat34.str().c_str(), &opaqueDecFloat34);
 
  407            return opaqueDecFloat34;
 
  417        std::byte stringToBoolean(std::string_view value)
 
  419            auto trimmed = value;
 
  421            while (!trimmed.empty() && std::isspace(
static_cast<unsigned char>(trimmed.front())))
 
  422                trimmed.remove_prefix(1);
 
  424            while (!trimmed.empty() && std::isspace(
static_cast<unsigned char>(trimmed.back())))
 
  425                trimmed.remove_suffix(1);
 
  427            std::string normalized{trimmed};
 
  428            std::transform(normalized.begin(), normalized.end(), normalized.begin(),
 
  429                [](
unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
 
  431            if (normalized == 
"true")
 
  433            else if (normalized == 
"false")
 
  436            throwConversionErrorFromString(std::string{value});
 
  440        double powerOfTen(
int scale) 
noexcept   
  449            static constexpr double UPPER_PART[] = {
 
  462            static constexpr double LOWER_PART[] = {
 
  497            assert((scale >= 0) && (scale < 320));
 
  499            const auto upper = UPPER_PART[scale >> 5];
 
  500            const auto lower = LOWER_PART[scale & 0x1F];
 
  502            return upper * lower;
 
  505        template <
typename T>
 
  506        void adjustScale(T& val, 
int scale, 
const T minLimit, 
const T maxLimit)
 
  515                        fraction = int(val % 10);
 
  521                else if (fraction < -4)
 
  523                    static_assert((-85 / 10 == -8) && (-85 % 10 == -5),
 
  524                        "If we port to a platform where ((-85 / 10 == -9) && (-85 % 10 == 5)), we'll have to change " 
  525                        "this depending on the platform");
 
  533                    if ((val > maxLimit / 10) || (val < minLimit / 10))
 
  534                        throwNumericOutOfRange();
 
  538                    if (val > maxLimit || val < minLimit)
 
  539                        throwNumericOutOfRange();
 
  544        template <
typename T>
 
  545        T conversionEpsilon()
 
  547            if constexpr (std::is_same_v<T, float>)
 
  548                return static_cast<T
>(1e-5f);
 
  549            else if constexpr (std::is_same_v<T, double>)
 
  550                return static_cast<T
>(1e-14);
 
  551#if FB_CPP_USE_BOOST_MULTIPRECISION != 0 
  552            else if constexpr (std::same_as<T, BoostDecFloat16> || std::same_as<T, BoostDecFloat34>)
 
  554                const auto epsilon = std::numeric_limits<T>::epsilon();
 
  555                return static_cast<T
>(epsilon * 
static_cast<T
>(10));
 
  559                return std::numeric_limits<T>::epsilon();
 
  564        StatusWrapper* statusWrapper;
 
boost::multiprecision::number< boost::multiprecision::cpp_dec_float< 34 > > BoostDecFloat34
34-digit decimal floating point using Boost.Multiprecision.
FB_DEC16 OpaqueDecFloat16
Opaque 16-digit decimal floating point exposed by the Firebird API.
boost::multiprecision::number< boost::multiprecision::cpp_dec_float< 16 > > BoostDecFloat16
16-digit decimal floating point using Boost.Multiprecision.
FB_I128 OpaqueInt128
Opaque 128-bit integer exposed by the Firebird API.
FB_DEC34 OpaqueDecFloat34
Opaque 34-digit decimal floating point exposed by the Firebird API.
boost::multiprecision::int128_t BoostInt128
128-bit integer using Boost.Multiprecision.