22#ifndef FBCPP_NUMERIC_CONVERTER_H
23#define FBCPP_NUMERIC_CONVERTER_H
49 struct NumberTypePriority
51 static constexpr int value = 0;
54#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
58 static constexpr int value = 7;
64 static constexpr int value = 6;
68#if FB_CPP_USE_BOOST_DECIMAL != 0
72 static constexpr int value = 10;
78 static constexpr int value = 9;
84 static constexpr int value = 8;
89 struct NumberTypePriority<double>
91 static constexpr int value = 6;
95 struct NumberTypePriority<float>
97 static constexpr int value = 5;
100#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
104 static constexpr int value = 4;
109 struct NumberTypePriority<std::int64_t>
111 static constexpr int value = 3;
115 struct NumberTypePriority<std::int32_t>
117 static constexpr int value = 2;
121 struct NumberTypePriority<std::int16_t>
123 static constexpr int value = 1;
126 template <
typename T>
127 inline constexpr int NumberTypePriorityValue =
128 NumberTypePriority<std::remove_cv_t<std::remove_reference_t<T>>>::value;
130 template <
typename T1,
typename T2>
131 using GreaterNumberType = std::conditional_t<(NumberTypePriorityValue<T1> >= NumberTypePriorityValue<T2>), T1, T2>;
133 template <
typename T>
134 inline constexpr bool IsFloatingNumber = std::is_floating_point_v<T>
135#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
136 || std::same_as<T, BoostDecFloat16> || std::same_as<T, BoostDecFloat34>
138#if FB_CPP_USE_BOOST_DECIMAL != 0
139 || std::same_as<T, BoostDecimal32> || std::same_as<T, BoostDecimal64> || std::same_as<T, BoostDecimal128>
143 template <
typename T>
144 concept FloatingNumber = IsFloatingNumber<T>;
146 template <
typename T>
147 concept IntegralNumber = std::is_integral_v<T>
148#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
149 || std::same_as<T, BoostInt128>
153 template <
typename T>
156 using type = std::make_unsigned_t<T>;
159#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
163 using type = boost::multiprecision::uint128_t;
167 template <
typename T>
168 using MakeUnsignedType =
typename MakeUnsigned<T>::type;
170 class NumericConverter final
173 explicit NumericConverter(Client& client)
178 [[noreturn]]
void throwNumericOutOfRange()
180 static constexpr std::intptr_t STATUS_NUMERIC_OUT_OF_RANGE[] = {
184 isc_numeric_out_of_range,
188 throw DatabaseException(*client, STATUS_NUMERIC_OUT_OF_RANGE);
191 [[noreturn]]
void throwConversionErrorFromString(
const std::string& str)
193 const std::intptr_t STATUS_CONVERSION_ERROR_FROM_STRING[] = {
197 reinterpret_cast<std::intptr_t
>(str.c_str()),
201 throw DatabaseException(*client, STATUS_CONVERSION_ERROR_FROM_STRING);
205 template <IntegralNumber To, IntegralNumber From>
206 To numberToNumber(
const ScaledNumber<From>& from,
int toScale)
208 const int scaleDiff = toScale - from.scale;
210 using ComputeType = GreaterNumberType<
decltype(from.value), To>;
212 ComputeType result =
static_cast<ComputeType
>(from.value);
216 adjustScale(result, scaleDiff,
static_cast<ComputeType
>(std::numeric_limits<To>::min()),
217 static_cast<ComputeType
>(std::numeric_limits<To>::max()));
220 if (result <
static_cast<ComputeType
>(std::numeric_limits<To>::min()) ||
221 result >
static_cast<ComputeType
>(std::numeric_limits<To>::max()))
223 throwNumericOutOfRange();
226 return static_cast<To
>(result);
229 template <IntegralNumber To, FloatingNumber From>
230 To numberToNumber(
const From& from,
int toScale)
232 using ComputeType = GreaterNumberType<double, From>;
234 if (isNonFinite(from))
235 throwNumericOutOfRange();
237 ComputeType value = convertFloatingValue<ComputeType>(from);
238 const ComputeType eps = conversionEpsilon<ComputeType>();
239 const ComputeType half{0.5};
242 value /= powerOfTen<ComputeType>(toScale);
243 else if (toScale < 0)
244 value *= powerOfTen<ComputeType>(-toScale);
251 static const auto minLimit = convertFloatingValue<ComputeType>(std::numeric_limits<To>::min());
252 static const auto maxLimit = convertFloatingValue<ComputeType>(std::numeric_limits<To>::max());
254 if (value < minLimit)
256 if (value > minLimit - ComputeType{1})
257 return std::numeric_limits<To>::min();
258 throwNumericOutOfRange();
261 if (value > maxLimit)
263 if (value < maxLimit + ComputeType{1})
264 return std::numeric_limits<To>::max();
265 throwNumericOutOfRange();
268 return convertIntegralValue<To>(value);
271 template <FloatingNumber To,
typename From>
272 To numberToNumber(
const ScaledNumber<From>& from,
int toScale = 0)
274 assert(toScale == 0);
276 using ComputeType = GreaterNumberType<double, To>;
278 ComputeType value = convertFloatingValue<ComputeType>(from.value);
282 if (std::abs(from.scale) > std::numeric_limits<To>::max_exponent10)
283 throwNumericOutOfRange();
286 value *= powerOfTen<ComputeType>(from.scale);
287 else if (from.scale < 0)
288 value /= powerOfTen<ComputeType>(-from.scale);
291 return static_cast<To
>(value);
294 template <FloatingNumber To, FloatingNumber From>
295 To numberToNumber(
const From& from,
int toScale = 0)
297 assert(toScale == 0);
299#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
300 if constexpr (std::same_as<To, BoostDecFloat16> && std::same_as<From, BoostDecFloat34>)
301 return boostDecFloat34ToBoostDecFloat16(from);
304 return convertFloatingValue<To>(from);
307 template <IntegralNumber From>
308 std::string numberToString(
const ScaledNumber<From>& from)
312 const bool isNegative = from.value < 0;
313 const bool isMinLimit = from.value == std::numeric_limits<
decltype(from.value)>::min();
315 using UnsignedType = MakeUnsignedType<
decltype(from.value)>;
317 auto unsignedValue = isMinLimit ?
static_cast<UnsignedType
>(-(from.value + 1)) + 1
318 :
static_cast<UnsignedType
>(isNegative ? -from.value : from.value);
324 buffer[digitCount++] =
static_cast<char>((unsignedValue % 10) +
'0');
326 }
while (unsignedValue > 0);
335 for (
int i = digitCount - 1; i >= 0; --i)
338 result.append(
static_cast<std::string::size_type
>(from.scale),
'0');
342 const int decimalPlaces = -from.scale;
344 if (decimalPlaces >=
static_cast<int>(digitCount))
347 const int leadingZeros = decimalPlaces - digitCount;
348 result.append(
static_cast<std::string::size_type
>(leadingZeros),
'0');
350 for (
int i = digitCount - 1; i >= 0; --i)
355 for (
int i = digitCount - 1; i >= decimalPlaces; --i)
360 for (
int i = decimalPlaces - 1; i >= 0; --i)
368 template <FloatingNumber From>
369 std::string numberToString(
const From& from)
371 if constexpr (std::is_floating_point_v<From>)
373 if (std::isnan(from))
375 if (std::isinf(from))
376 return from > 0 ?
"Infinity" :
"-Infinity";
377 return std::to_string(from);
379#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
380 else if constexpr (std::same_as<From, BoostDecFloat16> || std::same_as<From, BoostDecFloat34>)
382 if ((boost::multiprecision::isnan) (from))
384 if ((boost::multiprecision::isinf) (from))
385 return from > 0 ?
"Infinity" :
"-Infinity";
389#if FB_CPP_USE_BOOST_DECIMAL != 0
390 else if constexpr (std::same_as<From, BoostDecimal32> || std::same_as<From, BoostDecimal64> ||
391 std::same_as<From, BoostDecimal128>)
393 if ((boost::decimal::isnan) (from))
395 if ((boost::decimal::isinf) (from))
396 return from > 0 ?
"Infinity" :
"-Infinity";
397 return boost::decimal::to_string(from);
404 std::string opaqueInt128ToString(StatusWrapper* statusWrapper,
const OpaqueInt128& opaqueInt128,
int scale)
406 const auto int128Util = client->getInt128Util(statusWrapper);
407 char buffer[fb::IInt128::STRING_SIZE + 1];
408 int128Util->toString(statusWrapper, &opaqueInt128, scale,
static_cast<unsigned>(
sizeof(buffer)), buffer);
412 std::string opaqueDecFloat16ToString(StatusWrapper* statusWrapper,
const OpaqueDecFloat16& opaqueDecFloat16)
414 const auto decFloat16Util = client->getDecFloat16Util(statusWrapper);
415 char buffer[fb::IDecFloat16::STRING_SIZE + 1];
416 decFloat16Util->toString(statusWrapper, &opaqueDecFloat16,
static_cast<unsigned>(
sizeof(buffer)), buffer);
420 std::string opaqueDecFloat34ToString(StatusWrapper* statusWrapper,
const OpaqueDecFloat34& opaqueDecFloat34)
422 const auto decFloat34Util = client->getDecFloat34Util(statusWrapper);
423 char buffer[fb::IDecFloat34::STRING_SIZE + 1];
424 decFloat34Util->toString(statusWrapper, &opaqueDecFloat34,
static_cast<unsigned>(
sizeof(buffer)), buffer);
428#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
431 validateFirebirdInt128(boostInt128);
434 const auto value = boostInt128.str();
435 client->getInt128Util(statusWrapper)->fromString(statusWrapper, 0, value.c_str(), &opaqueInt128);
442 StatusWrapper status{*client};
443 return boostInt128ToOpaqueInt128(&status, boostInt128);
448 return BoostInt128{opaqueInt128ToString(statusWrapper, opaqueInt128, 0)};
453 StatusWrapper status{*client};
454 return opaqueInt128ToBoostInt128(&status, opaqueInt128);
460 const auto decFloat16Util = client->getDecFloat16Util(statusWrapper);
462 const auto value = numberToString(boostDecFloat16);
463 decFloat16Util->fromString(statusWrapper, value.c_str(), &opaqueDecFloat16);
464 return opaqueDecFloat16;
470 const auto value = opaqueDecFloat16ToString(statusWrapper, opaqueDecFloat16);
472 if (isSignalingNaN(value))
473 throw FbCppException(
"BoostDecFloat16 cannot represent a signaling NaN");
474 else if (value ==
"Infinity")
475 return std::numeric_limits<BoostDecFloat16>::infinity();
476 else if (value ==
"-Infinity")
477 return -std::numeric_limits<BoostDecFloat16>::infinity();
483 catch (
const std::exception&)
485 throwConversionErrorFromString(value);
492 const auto decFloat34Util = client->getDecFloat34Util(statusWrapper);
494 const auto value = numberToString(boostDecFloat34);
495 decFloat34Util->fromString(statusWrapper, value.c_str(), &opaqueDecFloat34);
496 return opaqueDecFloat34;
502 const auto value = opaqueDecFloat34ToString(statusWrapper, opaqueDecFloat34);
504 if (isSignalingNaN(value))
505 throw FbCppException(
"BoostDecFloat34 cannot represent a signaling NaN; use OpaqueDecFloat34");
506 else if (value ==
"Infinity")
507 return std::numeric_limits<BoostDecFloat34>::infinity();
508 else if (value ==
"-Infinity")
509 return -std::numeric_limits<BoostDecFloat34>::infinity();
515 catch (
const std::exception&)
517 throwConversionErrorFromString(value);
523 StatusWrapper status{*client};
525 const auto value = numberToString(boostDecFloat34);
526 client->getDecFloat16Util(&status)->fromString(&status, value.c_str(), &opaqueDecFloat16);
527 return opaqueDecFloat16ToBoostDecFloat16(&status, opaqueDecFloat16);
531#if FB_CPP_USE_BOOST_DECIMAL != 0
535 return stringToBoostDecimal<BoostDecimal32>(opaqueDecFloat16ToString(statusWrapper, opaqueDecFloat16));
541 return stringToBoostDecimal<BoostDecimal64>(opaqueDecFloat16ToString(statusWrapper, opaqueDecFloat16));
547 return stringToBoostDecimal<BoostDecimal128>(opaqueDecFloat16ToString(statusWrapper, opaqueDecFloat16));
553 return stringToBoostDecimal<BoostDecimal32>(opaqueDecFloat34ToString(statusWrapper, opaqueDecFloat34));
559 return stringToBoostDecimal<BoostDecimal64>(opaqueDecFloat34ToString(statusWrapper, opaqueDecFloat34));
565 return stringToBoostDecimal<BoostDecimal128>(opaqueDecFloat34ToString(statusWrapper, opaqueDecFloat34));
570 return boostDecimalToOpaqueDecFloat16(statusWrapper, value);
575 return boostDecimalToOpaqueDecFloat16(statusWrapper, value);
580 return boostDecimalToOpaqueDecFloat16(statusWrapper, value);
585 return boostDecimalToOpaqueDecFloat34(statusWrapper, value);
590 return boostDecimalToOpaqueDecFloat34(statusWrapper, value);
595 return boostDecimalToOpaqueDecFloat34(statusWrapper, value);
600 std::byte stringToBoolean(std::string_view value)
602 auto trimmed = value;
604 while (!trimmed.empty() && std::isspace(
static_cast<unsigned char>(trimmed.front())))
605 trimmed.remove_prefix(1);
607 while (!trimmed.empty() && std::isspace(
static_cast<unsigned char>(trimmed.back())))
608 trimmed.remove_suffix(1);
610 std::string normalized{trimmed};
611 std::transform(normalized.begin(), normalized.end(), normalized.begin(),
612 [](
unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
614 if (normalized ==
"true")
616 else if (normalized ==
"false")
619 throwConversionErrorFromString(std::string{value});
623 template <
typename T>
624 static bool isNonFinite(
const T& value)
626 using ValueType = std::remove_cvref_t<T>;
628 if constexpr (std::is_floating_point_v<ValueType>)
629 return std::isnan(value) || std::isinf(value);
630#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
631 else if constexpr (std::same_as<ValueType, BoostDecFloat16> || std::same_as<ValueType, BoostDecFloat34>)
632 return (boost::multiprecision::isnan) (value) || (boost::multiprecision::isinf) (value);
634#if FB_CPP_USE_BOOST_DECIMAL != 0
635 else if constexpr (std::same_as<ValueType, BoostDecimal32> || std::same_as<ValueType, BoostDecimal64> ||
636 std::same_as<ValueType, BoostDecimal128>)
637 return (boost::decimal::isnan) (value) || (boost::decimal::isinf) (value);
643 template <
typename T>
644 static bool isSignalingNaNValue(
const T& value)
646#if FB_CPP_USE_BOOST_MULTIPRECISION != 0 || FB_CPP_USE_BOOST_DECIMAL != 0
647 using ValueType = std::remove_cvref_t<T>;
650#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
651 if constexpr (std::same_as<ValueType, BoostDecFloat16> || std::same_as<ValueType, BoostDecFloat34>)
652 return (boost::multiprecision::isnan) (value) && isSignalingNaN(value.str());
654#if FB_CPP_USE_BOOST_DECIMAL != 0
655 if constexpr (std::same_as<ValueType, BoostDecimal32> || std::same_as<ValueType, BoostDecimal64> ||
656 std::same_as<ValueType, BoostDecimal128>)
657 return (boost::decimal::issignaling) (value);
662 template <
typename To,
typename From>
663 To convertFloatingValue(
const From& from)
665 using ToType = std::remove_cvref_t<To>;
666 using FromType = std::remove_cvref_t<From>;
668 if constexpr (std::same_as<ToType, FromType>)
670#if FB_CPP_USE_BOOST_DECIMAL != 0
671 else if constexpr (std::same_as<ToType, BoostDecimal32> || std::same_as<ToType, BoostDecimal64> ||
672 std::same_as<ToType, BoostDecimal128>)
674 if (isSignalingNaNValue(from))
675 return std::numeric_limits<ToType>::signaling_NaN();
676 if constexpr (std::is_floating_point_v<FromType>)
677 return ToType{std::format(
"{:.16e}", from)};
678#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
679 else if constexpr (std::same_as<FromType, BoostDecFloat16> || std::same_as<FromType, BoostDecFloat34>)
681 if ((boost::multiprecision::isnan) (from))
682 return std::numeric_limits<ToType>::quiet_NaN();
683 if ((boost::multiprecision::isinf) (from))
685 return from > 0 ? std::numeric_limits<ToType>::infinity()
686 : -std::numeric_limits<ToType>::infinity();
688 return ToType{from.str()};
690 else if constexpr (std::same_as<FromType, BoostInt128>)
691 return ToType{from.str()};
694 return static_cast<ToType
>(from);
697#if FB_CPP_USE_BOOST_MULTIPRECISION != 0 && FB_CPP_USE_BOOST_DECIMAL != 0
698 else if constexpr ((std::same_as<ToType, BoostDecFloat16> || std::same_as<ToType, BoostDecFloat34>) &&
699 (std::same_as<FromType, BoostDecimal32> || std::same_as<FromType, BoostDecimal64> ||
700 std::same_as<FromType, BoostDecimal128>) )
702 if (isSignalingNaNValue(from))
703 throw FbCppException(
"Boost.Multiprecision cannot represent a signaling NaN");
704 if ((boost::decimal::isnan) (from))
705 return ToType{
"NaN"};
706 if ((boost::decimal::isinf) (from))
708 return from > 0 ? std::numeric_limits<ToType>::infinity()
709 : -std::numeric_limits<ToType>::infinity();
711 return ToType{boost::decimal::to_string(from)};
715 return static_cast<ToType
>(from);
718 template <
typename To,
typename From>
719 To convertIntegralValue(
const From& from)
721 using ToType = std::remove_cvref_t<To>;
723#if FB_CPP_USE_BOOST_DECIMAL != 0
724 using FromType = std::remove_cvref_t<From>;
726 if constexpr (std::same_as<FromType, BoostDecimal32> || std::same_as<FromType, BoostDecimal64> ||
727 std::same_as<FromType, BoostDecimal128>)
729#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
730 if constexpr (std::same_as<ToType, BoostInt128>)
733 const auto result = boost::decimal::to_chars(
734 buffer, buffer +
sizeof(buffer), from, boost::decimal::chars_format::fixed);
736 if (result.ec != std::errc{})
737 throwNumericOutOfRange();
739 std::string value{buffer, result.ptr};
740 if (
const auto decimalPoint = value.find(
'.'); decimalPoint != std::string::npos)
741 value.erase(decimalPoint);
743 return ToType{value};
747 if constexpr (std::same_as<ToType, bool>)
748 return static_cast<ToType
>(
static_cast<bool>(from));
749 else if constexpr (std::is_signed_v<ToType>)
750 return static_cast<ToType
>(
static_cast<long long>(from));
752 return static_cast<ToType
>(
static_cast<unsigned long long>(from));
756 return static_cast<ToType
>(from);
759 static bool isSignalingNaN(std::string_view value)
761 std::string normalized{value};
762 if (!normalized.empty() && (normalized.front() ==
'+' || normalized.front() ==
'-'))
763 normalized.erase(0, 1);
765 std::transform(normalized.begin(), normalized.end(), normalized.begin(),
766 [](
unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
768 return normalized.starts_with(
"snan");
771#if FB_CPP_USE_BOOST_DECIMAL != 0
772 template <
typename T>
773 T stringToBoostDecimal(
const std::string& value)
775 if (isSignalingNaN(value))
776 return std::numeric_limits<T>::signaling_NaN();
777 else if (value ==
"Infinity")
778 return std::numeric_limits<T>::infinity();
779 else if (value ==
"-Infinity")
780 return -std::numeric_limits<T>::infinity();
786 catch (
const std::exception&)
788 throwConversionErrorFromString(value);
792 template <
typename T>
793 OpaqueDecFloat16 boostDecimalToOpaqueDecFloat16(StatusWrapper* statusWrapper,
const T& value)
795 if (isSignalingNaNValue(value))
796 throw FbCppException(
"Boost.Decimal cannot represent a signaling NaN; use OpaqueDecFloat16");
799 const auto stringValue = numberToString(value);
800 client->getDecFloat16Util(statusWrapper)->fromString(statusWrapper, stringValue.c_str(), &result);
804 template <
typename T>
805 OpaqueDecFloat34 boostDecimalToOpaqueDecFloat34(StatusWrapper* statusWrapper,
const T& value)
807 if (isSignalingNaNValue(value))
808 throw FbCppException(
"Boost.Decimal cannot represent a signaling NaN; use OpaqueDecFloat34");
811 const auto stringValue = numberToString(value);
812 client->getDecFloat34Util(statusWrapper)->fromString(statusWrapper, stringValue.c_str(), &result);
817 double powerOfTenDouble(
int scale)
noexcept
819 static constexpr double UPPER_PART[] = {
832 static constexpr double LOWER_PART[] = {
867 assert((scale >= 0) && (scale < 320));
869 const auto upper = UPPER_PART[scale >> 5];
870 const auto lower = LOWER_PART[scale & 0x1F];
872 return upper * lower;
875 template <
typename T>
876 T powerOfTen(
int scale)
878 assert((scale >= 0) && (scale < 320));
880 if constexpr (std::same_as<T, float> || std::same_as<T, double>)
881 return static_cast<T
>(powerOfTenDouble(scale));
886 for (
int i = 0; i < scale; ++i)
893#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
894 void validateFirebirdInt128(
const BoostInt128& value)
899 if (value < FB_MIN_INT128 || value > FB_MAX_INT128)
900 throwNumericOutOfRange();
905 template <
typename T>
906 void adjustScale(T& val,
int scale,
const T minLimit,
const T maxLimit)
915 fraction = int(val % 10);
921 else if (fraction < -4)
923 static_assert((-85 / 10 == -8) && (-85 % 10 == -5),
924 "If we port to a platform where ((-85 / 10 == -9) && (-85 % 10 == 5)), we'll have to change "
925 "this depending on the platform");
933 if ((val > maxLimit / 10) || (val < minLimit / 10))
934 throwNumericOutOfRange();
938 if (val > maxLimit || val < minLimit)
939 throwNumericOutOfRange();
944 template <
typename T>
945 T conversionEpsilon()
947 if constexpr (std::is_same_v<T, float>)
948 return static_cast<T
>(1e-5f);
949 else if constexpr (std::is_same_v<T, double>)
950 return static_cast<T
>(1e-14);
951#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
952 else if constexpr (std::same_as<T, BoostDecFloat16> || std::same_as<T, BoostDecFloat34>)
954 const auto epsilon = std::numeric_limits<T>::epsilon();
955 return static_cast<T
>(epsilon *
static_cast<T
>(10));
958#if FB_CPP_USE_BOOST_DECIMAL != 0
959 else if constexpr (std::same_as<T, BoostDecimal32> || std::same_as<T, BoostDecimal64> ||
960 std::same_as<T, BoostDecimal128>)
962 const auto epsilon = std::numeric_limits<T>::epsilon();
963 return epsilon *
static_cast<T
>(10);
967 return std::numeric_limits<T>::epsilon();
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.
boost::decimal::decimal128_t BoostDecimal128
34-digit decimal floating point using Boost.Decimal.
boost::decimal::decimal32_t BoostDecimal32
7-digit decimal floating point using Boost.Decimal.
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.
boost::decimal::decimal64_t BoostDecimal64
16-digit decimal floating point using Boost.Decimal.