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)
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 if constexpr (std::is_floating_point_v<From>)
209 if (std::isnan(from) || std::isinf(from))
210 throwNumericOutOfRange();
213 ComputeType value{from};
214 const ComputeType eps = conversionEpsilon<ComputeType>();
217 value /= powerOfTen(toScale);
218 else if (toScale < 0)
219 value *= powerOfTen(-toScale);
226 static const auto minLimit =
static_cast<ComputeType
>(std::numeric_limits<To>::min());
227 static const auto maxLimit =
static_cast<ComputeType
>(std::numeric_limits<To>::max());
229 if (value < minLimit)
231 if (value > minLimit - 1.0f)
232 return std::numeric_limits<To>::min();
233 throwNumericOutOfRange();
236 if (value > maxLimit)
238 if (value < maxLimit + 1.0f)
239 return std::numeric_limits<To>::max();
240 throwNumericOutOfRange();
243 return static_cast<To
>(value);
246 template <FloatingNumber To,
typename From>
247 To numberToNumber(
const ScaledNumber<From>& from,
int toScale = 0)
249 assert(toScale == 0);
251 using ComputeType = GreaterNumberType<double, To>;
253 ComputeType value =
static_cast<ComputeType
>(from.value);
257 if (std::abs(from.scale) > std::numeric_limits<To>::max_exponent10)
258 throwNumericOutOfRange();
261 value *= powerOfTen(from.scale);
262 else if (from.scale < 0)
263 value /= powerOfTen(-from.scale);
266 return static_cast<To
>(value);
269 template <FloatingNumber To, FloatingNumber From>
270 To numberToNumber(
const From& from,
int toScale = 0)
272 assert(toScale == 0);
274 if constexpr (std::is_floating_point_v<From> && !std::is_floating_point_v<To>)
275 return To{std::format(
"{:.16e}", from)};
277 return static_cast<To
>(from);
280 template <IntegralNumber From>
281 std::string numberToString(
const ScaledNumber<From>& from)
285 const bool isNegative = from.value < 0;
286 const bool isMinLimit = from.value == std::numeric_limits<
decltype(from.value)>::min();
288 using UnsignedType = MakeUnsignedType<
decltype(from.value)>;
290 auto unsignedValue = isMinLimit ?
static_cast<UnsignedType
>(-(from.value + 1)) + 1
291 :
static_cast<UnsignedType
>(isNegative ? -from.value : from.value);
297 buffer[digitCount++] =
static_cast<char>((unsignedValue % 10) +
'0');
299 }
while (unsignedValue > 0);
308 for (
int i = digitCount - 1; i >= 0; --i)
311 result.append(
static_cast<std::string::size_type
>(from.scale),
'0');
315 const int decimalPlaces = -from.scale;
317 if (decimalPlaces >=
static_cast<int>(digitCount))
320 const int leadingZeros = decimalPlaces - digitCount;
321 result.append(
static_cast<std::string::size_type
>(leadingZeros),
'0');
323 for (
int i = digitCount - 1; i >= 0; --i)
328 for (
int i = digitCount - 1; i >= decimalPlaces; --i)
333 for (
int i = decimalPlaces - 1; i >= 0; --i)
341 template <FloatingNumber From>
342 std::string numberToString(
const From& from)
344 if constexpr (std::is_floating_point_v<From>)
346 if (std::isnan(from))
348 if (std::isinf(from))
349 return from > 0 ?
"Infinity" :
"-Infinity";
350 return std::to_string(from);
356 std::string opaqueInt128ToString(StatusWrapper* statusWrapper,
const OpaqueInt128& opaqueInt128,
int scale)
358 const auto int128Util = client->getInt128Util(statusWrapper);
359 char buffer[fb::IInt128::STRING_SIZE + 1];
360 int128Util->toString(statusWrapper, &opaqueInt128, scale,
static_cast<unsigned>(
sizeof(buffer)), buffer);
364 std::string opaqueDecFloat16ToString(StatusWrapper* statusWrapper,
const OpaqueDecFloat16& opaqueDecFloat16)
366 const auto decFloat16Util = client->getDecFloat16Util(statusWrapper);
367 char buffer[fb::IDecFloat16::STRING_SIZE + 1];
368 decFloat16Util->toString(statusWrapper, &opaqueDecFloat16,
static_cast<unsigned>(
sizeof(buffer)), buffer);
372 std::string opaqueDecFloat34ToString(StatusWrapper* statusWrapper,
const OpaqueDecFloat34& opaqueDecFloat34)
374 const auto decFloat34Util = client->getDecFloat34Util(statusWrapper);
375 char buffer[fb::IDecFloat34::STRING_SIZE + 1];
376 decFloat34Util->toString(statusWrapper, &opaqueDecFloat34,
static_cast<unsigned>(
sizeof(buffer)), buffer);
380#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
383 const boost::multiprecision::uint128_t boostUInt128{boostInt128};
386 opaqueInt128.fb_data[0] =
static_cast<std::uint64_t
>(boostUInt128 & 0xFFFFFFFFFFFFFFFFULL);
387 opaqueInt128.fb_data[1] =
static_cast<std::uint64_t
>(boostUInt128 >> 64);
394 const auto high =
static_cast<std::int64_t
>(opaqueInt128.fb_data[1]);
397 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;
413 return BoostDecFloat16{opaqueDecFloat16ToString(statusWrapper, opaqueDecFloat16)};
419 const auto decFloat34Util = client->getDecFloat34Util(statusWrapper);
421 decFloat34Util->fromString(statusWrapper, boostDecFloat34.str().c_str(), &opaqueDecFloat34);
422 return opaqueDecFloat34;
428 return BoostDecFloat34{opaqueDecFloat34ToString(statusWrapper, opaqueDecFloat34)};
433 std::byte stringToBoolean(std::string_view value)
435 auto trimmed = value;
437 while (!trimmed.empty() && std::isspace(
static_cast<unsigned char>(trimmed.front())))
438 trimmed.remove_prefix(1);
440 while (!trimmed.empty() && std::isspace(
static_cast<unsigned char>(trimmed.back())))
441 trimmed.remove_suffix(1);
443 std::string normalized{trimmed};
444 std::transform(normalized.begin(), normalized.end(), normalized.begin(),
445 [](
unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
447 if (normalized ==
"true")
449 else if (normalized ==
"false")
452 throwConversionErrorFromString(std::string{value});
456 double powerOfTen(
int scale)
noexcept
465 static constexpr double UPPER_PART[] = {
478 static constexpr double LOWER_PART[] = {
513 assert((scale >= 0) && (scale < 320));
515 const auto upper = UPPER_PART[scale >> 5];
516 const auto lower = LOWER_PART[scale & 0x1F];
518 return upper * lower;
521 template <
typename T>
522 void adjustScale(T& val,
int scale,
const T minLimit,
const T maxLimit)
531 fraction = int(val % 10);
537 else if (fraction < -4)
539 static_assert((-85 / 10 == -8) && (-85 % 10 == -5),
540 "If we port to a platform where ((-85 / 10 == -9) && (-85 % 10 == 5)), we'll have to change "
541 "this depending on the platform");
549 if ((val > maxLimit / 10) || (val < minLimit / 10))
550 throwNumericOutOfRange();
554 if (val > maxLimit || val < minLimit)
555 throwNumericOutOfRange();
560 template <
typename T>
561 T conversionEpsilon()
563 if constexpr (std::is_same_v<T, float>)
564 return static_cast<T
>(1e-5f);
565 else if constexpr (std::is_same_v<T, double>)
566 return static_cast<T
>(1e-14);
567#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
568 else if constexpr (std::same_as<T, BoostDecFloat16> || std::same_as<T, BoostDecFloat34>)
570 const auto epsilon = std::numeric_limits<T>::epsilon();
571 return static_cast<T
>(epsilon *
static_cast<T
>(10));
575 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.
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.