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.