sparrow 0.9.0
Loading...
Searching...
No Matches
decimal.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cmath>
4#include <iostream>
5#include <sstream>
6
7#if defined(__cpp_lib_format)
8# include <format>
9#endif
10
13
14namespace sparrow
15{
23 template <typename T>
24 concept decimal_integer_type = std::is_same_v<T, std::int32_t> || std::is_same_v<T, std::int64_t>
25 || std::is_same_v<T, int128_t> || std::is_same_v<T, int256_t>;
26
60 template <decimal_integer_type T>
61 class decimal
62 {
63 public:
64
65 using integer_type = T;
66
77 constexpr decimal()
78 requires(!is_int_placeholder_v<T>);
79
90 constexpr decimal()
92
105 constexpr decimal(T value, int scale);
106
120 constexpr bool operator==(const decimal& other) const;
121
137 constexpr explicit operator float() const
138 requires(!is_int_placeholder_v<T>);
139
155 constexpr explicit operator double() const
156 requires(!is_int_placeholder_v<T>);
157
173 constexpr explicit operator long double() const
174 requires(!is_int_placeholder_v<T>);
175
191 [[nodiscard]] explicit operator std::string() const
192 requires(!is_int_placeholder_v<T>);
193
201 [[nodiscard]] constexpr T storage() const;
202
212 [[nodiscard]] constexpr int scale() const;
213
214 private:
215
231 template <class FLOAT_TYPE>
232 [[nodiscard]] constexpr FLOAT_TYPE convert_to_floating_point() const
233 requires(!is_int_placeholder_v<T>);
234
235 T m_value;
236 int m_scale;
237 };
238
244 template <typename T>
245 constexpr bool is_decimal_v = mpl::is_type_instance_of_v<T, decimal>;
246
255 template <typename T>
257
258 template <decimal_integer_type T>
259 constexpr decimal<T>::decimal()
260 requires(!is_int_placeholder_v<T>)
261 : m_value(0)
262 , m_scale()
263 {
264 }
265
266 template <decimal_integer_type T>
267 constexpr decimal<T>::decimal()
269 : m_value()
270 , m_scale()
271 {
272 }
273
274 template <decimal_integer_type T>
275 constexpr decimal<T>::decimal(T value, int scale)
276 : m_value(value)
277 , m_scale(scale)
278 {
279 }
280
281 template <decimal_integer_type T>
282 constexpr bool decimal<T>::operator==(const decimal& other) const
283 {
284 return m_value == other.m_value && m_scale == other.m_scale;
285 }
286
287 template <decimal_integer_type T>
288 constexpr decimal<T>::operator float() const
289 requires(!is_int_placeholder_v<T>)
290 {
291 return convert_to_floating_point<float>();
292 }
293
294 template <decimal_integer_type T>
295 constexpr decimal<T>::operator double() const
296 requires(!is_int_placeholder_v<T>)
297 {
298 return convert_to_floating_point<double>();
299 }
300
301 template <decimal_integer_type T>
302 constexpr decimal<T>::operator long double() const
303 requires(!is_int_placeholder_v<T>)
304 {
305 return convert_to_floating_point<long double>();
306 }
307
308 template <decimal_integer_type T>
309 decimal<T>::operator std::string() const
310 requires(!is_int_placeholder_v<T>)
311 {
312 std::stringstream ss;
313 ss << m_value;
314 std::string result = ss.str();
315 if (m_scale == 0)
316 {
317 return result;
318 }
319 if (result[0] == '0')
320 {
321 return "0";
322 }
323 // remove - (we handle it later)
324 if (result[0] == '-')
325 {
326 result = result.substr(1);
327 }
328
329 if (m_scale > 0)
330 {
331 if (result.length() <= static_cast<std::size_t>(m_scale))
332 {
333 result.insert(
334 0,
335 std::string(static_cast<std::size_t>(m_scale) + 1 - result.length(), '0')
336 ); // Pad with leading zeros
337 }
338 std::size_t int_part_len = result.length() - static_cast<std::size_t>(m_scale);
339 std::string int_part = result.substr(0, int_part_len);
340 std::string frac_part = result.substr(int_part_len);
341 result = int_part + "." + frac_part;
342 }
343 else
344 {
345 result += std::string(static_cast<std::size_t>(-m_scale), '0'); // Append zeros
346 }
347 // handle negative values
348 if (m_value < 0)
349 {
350 result.insert(0, 1, '-');
351 }
352 return result;
353 }
354
355 template <decimal_integer_type T>
356 constexpr T decimal<T>::storage() const
357 {
358 return m_value;
359 }
360
361 template <decimal_integer_type T>
362 constexpr int decimal<T>::scale() const
363 {
364 return m_scale;
365 }
366
367 template <decimal_integer_type T>
368 template <class FLOAT_TYPE>
369 constexpr FLOAT_TYPE decimal<T>::convert_to_floating_point() const
370 requires(!is_int_placeholder_v<T>)
371 {
372 using to_type = FLOAT_TYPE;
373 if constexpr (std::is_same_v<T, int256_t>)
374 {
375 // danger zone
376 auto val = static_cast<int128_t>(m_value);
377 return static_cast<to_type>(val) / static_cast<to_type>(std::pow(10, m_scale));
378 }
379 else
380 {
381 return static_cast<to_type>(m_value) / static_cast<to_type>(std::pow(10, m_scale));
382 }
383 }
384
385} // namespace sparrow
386
387#if defined(__cpp_lib_format)
388
389template <typename T>
390struct std::formatter<sparrow::decimal<T>>
391{
392 constexpr auto parse(std::format_parse_context& ctx)
393 {
394 return ctx.begin();
395 }
396
397 auto format(const sparrow::decimal<T>& d, std::format_context& ctx) const
398 {
399 return std::format_to(ctx.out(), "Decimal({}, {})", d.storage(), d.scale());
400 }
401};
402
403template <typename T>
404std::ostream& operator<<(std::ostream& os, const sparrow::decimal<T>& value)
405{
406 os << std::format("{}", value);
407 return os;
408}
409
410#endif
constexpr decimal()
Default constructor for placeholder integer types.
constexpr decimal(T value, int scale)
Constructor from value and scale.
Definition decimal.hpp:275
constexpr int scale() const
constexpr bool operator==(const decimal &other) const
Equality comparison operator.
Definition decimal.hpp:282
constexpr decimal()
Default constructor for non-placeholder integer types.
Definition decimal.hpp:259
Concept for valid decimal integer backing types.
Definition decimal.hpp:24
Concept for valid decimal types.
Definition decimal.hpp:256
primesum::int128_t int128_t
Definition large_int.hpp:84
constexpr bool is_int_placeholder_v
Definition large_int.hpp:82
constexpr bool is_decimal_v
Type trait to check if a type is a decimal instantiation.
Definition decimal.hpp:245
std::ostream & operator<<(std::ostream &os, const sparrow::nullval_t &)