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 = 8;
64 static constexpr int value = 7;
69 struct NumberTypePriority<double>
71 static constexpr int value = 6;
75 struct NumberTypePriority<float>
77 static constexpr int value = 5;
80#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
84 static constexpr int value = 4;
89 struct NumberTypePriority<std::int64_t>
91 static constexpr int value = 3;
95 struct NumberTypePriority<std::int32_t>
97 static constexpr int value = 2;
101 struct NumberTypePriority<std::int16_t>
103 static constexpr int value = 1;
106 template <
typename T>
107 inline constexpr int NumberTypePriorityValue =
108 NumberTypePriority<std::remove_cv_t<std::remove_reference_t<T>>>::value;
110 template <
typename T1,
typename T2>
111 using GreaterNumberType = std::conditional_t<(NumberTypePriorityValue<T1> >= NumberTypePriorityValue<T2>), T1, T2>;
113 template <
typename T>
114 inline constexpr bool IsFloatingNumber = std::is_floating_point_v<T>
115#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
116 || std::same_as<T, BoostDecFloat16> || std::same_as<T, BoostDecFloat34>
120 template <
typename T>
121 concept FloatingNumber = IsFloatingNumber<T>;
123 template <
typename T>
124 concept IntegralNumber = std::is_integral_v<T>
125#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
126 || std::same_as<T, BoostInt128>
130 template <
typename T>
133 using type = std::make_unsigned_t<T>;
136#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
140 using type = boost::multiprecision::uint128_t;
144 template <
typename T>
145 using MakeUnsignedType =
typename MakeUnsigned<T>::type;
147 class NumericConverter final
150 explicit NumericConverter(Client& client, StatusWrapper* statusWrapper)
152 statusWrapper{statusWrapper}
156 [[noreturn]]
void throwNumericOutOfRange()
158 static constexpr std::intptr_t STATUS_NUMERIC_OUT_OF_RANGE[] = {
160 isc_numeric_out_of_range,
164 throw DatabaseException(client, STATUS_NUMERIC_OUT_OF_RANGE);
167 [[noreturn]]
void throwConversionErrorFromString(
const std::string& str)
169 const std::intptr_t STATUS_CONVERSION_ERROR_FROM_STRING[] = {
171 reinterpret_cast<std::intptr_t
>(str.c_str()),
175 throw DatabaseException(client, STATUS_CONVERSION_ERROR_FROM_STRING);
179 template <IntegralNumber To, IntegralNumber From>
180 To numberToNumber(
const ScaledNumber<From>& from,
int toScale)
182 const int scaleDiff = toScale - from.scale;
184 using ComputeType = GreaterNumberType<
decltype(from.value), To>;
186 ComputeType result =
static_cast<ComputeType
>(from.value);
190 adjustScale(result, scaleDiff,
static_cast<ComputeType
>(std::numeric_limits<To>::min()),
191 static_cast<ComputeType
>(std::numeric_limits<To>::max()));
194 if (result <
static_cast<ComputeType
>(std::numeric_limits<To>::min()) ||
195 result >
static_cast<ComputeType
>(std::numeric_limits<To>::max()))
197 throwNumericOutOfRange();
200 return static_cast<To
>(result);
203 template <IntegralNumber To, FloatingNumber From>
204 To numberToNumber(
const From& from,
int toScale)
206 using ComputeType = GreaterNumberType<double, From>;
208 if constexpr (std::is_floating_point_v<From>)
210 if (std::isnan(from) || std::isinf(from))
211 throwNumericOutOfRange();
214 ComputeType value{from};
215 const ComputeType eps = conversionEpsilon<ComputeType>();
218 value /= powerOfTen(toScale);
219 else if (toScale < 0)
220 value *= powerOfTen(-toScale);
227 static const auto minLimit =
static_cast<ComputeType
>(std::numeric_limits<To>::min());
228 static const auto maxLimit =
static_cast<ComputeType
>(std::numeric_limits<To>::max());
230 if (value < minLimit)
232 if (value > minLimit - 1.0f)
233 return std::numeric_limits<To>::min();
234 throwNumericOutOfRange();
237 if (value > maxLimit)
239 if (value < maxLimit + 1.0f)
240 return std::numeric_limits<To>::max();
241 throwNumericOutOfRange();
244 return static_cast<To
>(value);
247 template <FloatingNumber To,
typename From>
248 To numberToNumber(
const ScaledNumber<From>& from,
int toScale = 0)
250 assert(toScale == 0);
252 using ComputeType = GreaterNumberType<double, To>;
254 ComputeType value =
static_cast<ComputeType
>(from.value);
258 if (std::abs(from.scale) > std::numeric_limits<To>::max_exponent10)
259 throwNumericOutOfRange();
262 value *= powerOfTen(from.scale);
263 else if (from.scale < 0)
264 value /= powerOfTen(-from.scale);
267 return static_cast<To
>(value);
270 template <FloatingNumber To, FloatingNumber From>
271 To numberToNumber(
const From& from,
int toScale = 0)
273 assert(toScale == 0);
275 if constexpr (std::is_floating_point_v<From> && !std::is_floating_point_v<To>)
276 return To{std::format(
"{:.16e}", from)};
278 return static_cast<To
>(from);
281 template <IntegralNumber From>
282 std::string numberToString(
const ScaledNumber<From>& from)
286 const bool isNegative = from.value < 0;
287 const bool isMinLimit = from.value == std::numeric_limits<
decltype(from.value)>::min();
289 using UnsignedType = MakeUnsignedType<
decltype(from.value)>;
291 auto unsignedValue = isMinLimit ?
static_cast<UnsignedType
>(-(from.value + 1)) + 1
292 :
static_cast<UnsignedType
>(isNegative ? -from.value : from.value);
298 buffer[digitCount++] =
static_cast<char>((unsignedValue % 10) +
'0');
300 }
while (unsignedValue > 0);
309 for (
int i = digitCount - 1; i >= 0; --i)
312 result.append(
static_cast<std::string::size_type
>(from.scale),
'0');
316 const int decimalPlaces = -from.scale;
318 if (decimalPlaces >=
static_cast<int>(digitCount))
321 const int leadingZeros = decimalPlaces - digitCount;
322 result.append(
static_cast<std::string::size_type
>(leadingZeros),
'0');
324 for (
int i = digitCount - 1; i >= 0; --i)
329 for (
int i = digitCount - 1; i >= decimalPlaces; --i)
334 for (
int i = decimalPlaces - 1; i >= 0; --i)
342 template <FloatingNumber From>
343 std::string numberToString(
const From& from)
345 if constexpr (std::is_floating_point_v<From>)
347 if (std::isnan(from))
349 if (std::isinf(from))
350 return from > 0 ?
"Infinity" :
"-Infinity";
351 return std::to_string(from);
357 std::string opaqueInt128ToString(
const OpaqueInt128& opaqueInt128,
int scale)
359 const auto int128Util = client.getUtil()->getInt128(statusWrapper);
360 char buffer[fb::IInt128::STRING_SIZE + 1];
361 int128Util->toString(statusWrapper, &opaqueInt128, scale,
static_cast<unsigned>(
sizeof(buffer)), buffer);
365 std::string opaqueDecFloat16ToString(
const OpaqueDecFloat16& opaqueDecFloat16)
367 const auto decFloat16Util = client.getDecFloat16Util(statusWrapper);
368 char buffer[fb::IDecFloat16::STRING_SIZE + 1];
369 decFloat16Util->toString(statusWrapper, &opaqueDecFloat16,
static_cast<unsigned>(
sizeof(buffer)), buffer);
373 std::string opaqueDecFloat34ToString(
const OpaqueDecFloat34& opaqueDecFloat34)
375 const auto decFloat34Util = client.getDecFloat34Util(statusWrapper);
376 char buffer[fb::IDecFloat34::STRING_SIZE + 1];
377 decFloat34Util->toString(statusWrapper, &opaqueDecFloat34,
static_cast<unsigned>(
sizeof(buffer)), buffer);
381#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
384 const boost::multiprecision::uint128_t boostUInt128{boostInt128};
387 opaqueInt128.fb_data[0] =
static_cast<std::uint64_t
>(boostUInt128 & 0xFFFFFFFFFFFFFFFFULL);
388 opaqueInt128.fb_data[1] =
static_cast<std::uint64_t
>(boostUInt128 >> 64);
395 const auto high =
static_cast<std::int64_t
>(opaqueInt128.fb_data[1]);
398 boostInt128 +=
static_cast<BoostInt128>(opaqueInt128.fb_data[0]);
404 const auto decFloat16Util = client.getDecFloat16Util(statusWrapper);
406 decFloat16Util->fromString(statusWrapper, boostDecFloat16.str().c_str(), &opaqueDecFloat16);
407 return opaqueDecFloat16;
417 const auto decFloat34Util = client.getDecFloat34Util(statusWrapper);
419 decFloat34Util->fromString(statusWrapper, boostDecFloat34.str().c_str(), &opaqueDecFloat34);
420 return opaqueDecFloat34;
430 std::byte stringToBoolean(std::string_view value)
432 auto trimmed = value;
434 while (!trimmed.empty() && std::isspace(
static_cast<unsigned char>(trimmed.front())))
435 trimmed.remove_prefix(1);
437 while (!trimmed.empty() && std::isspace(
static_cast<unsigned char>(trimmed.back())))
438 trimmed.remove_suffix(1);
440 std::string normalized{trimmed};
441 std::transform(normalized.begin(), normalized.end(), normalized.begin(),
442 [](
unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
444 if (normalized ==
"true")
446 else if (normalized ==
"false")
449 throwConversionErrorFromString(std::string{value});
453 double powerOfTen(
int scale)
noexcept
462 static constexpr double UPPER_PART[] = {
475 static constexpr double LOWER_PART[] = {
510 assert((scale >= 0) && (scale < 320));
512 const auto upper = UPPER_PART[scale >> 5];
513 const auto lower = LOWER_PART[scale & 0x1F];
515 return upper * lower;
518 template <
typename T>
519 void adjustScale(T& val,
int scale,
const T minLimit,
const T maxLimit)
528 fraction = int(val % 10);
534 else if (fraction < -4)
536 static_assert((-85 / 10 == -8) && (-85 % 10 == -5),
537 "If we port to a platform where ((-85 / 10 == -9) && (-85 % 10 == 5)), we'll have to change "
538 "this depending on the platform");
546 if ((val > maxLimit / 10) || (val < minLimit / 10))
547 throwNumericOutOfRange();
551 if (val > maxLimit || val < minLimit)
552 throwNumericOutOfRange();
557 template <
typename T>
558 T conversionEpsilon()
560 if constexpr (std::is_same_v<T, float>)
561 return static_cast<T
>(1e-5f);
562 else if constexpr (std::is_same_v<T, double>)
563 return static_cast<T
>(1e-14);
564#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
565 else if constexpr (std::same_as<T, BoostDecFloat16> || std::same_as<T, BoostDecFloat34>)
567 const auto epsilon = std::numeric_limits<T>::epsilon();
568 return static_cast<T
>(epsilon *
static_cast<T
>(10));
572 return std::numeric_limits<T>::epsilon();
577 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.