sparrow 0.6.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{
16 template <typename T>
17 concept decimal_integer_type = std::is_same_v<T, std::int32_t> || std::is_same_v<T, std::int64_t>
18 || std::is_same_v<T, int128_t> || std::is_same_v<T, int256_t>;
19
20 template <decimal_integer_type T>
21 class decimal
22 {
23 public:
24
25 using integer_type = T;
26
28 requires(!is_int_placeholder_v<T>);
31 decimal(T value, int scale);
32
33 bool operator==(const decimal& other) const;
34 bool operator!=(const decimal& other) const;
35 explicit operator float() const
36 requires(!is_int_placeholder_v<T>);
37 explicit operator double() const
38 requires(!is_int_placeholder_v<T>);
39 explicit operator long double() const
40 requires(!is_int_placeholder_v<T>);
41
42 // convert to string
43 [[nodiscard]] explicit operator std::string() const
44 requires(!is_int_placeholder_v<T>);
45
46 [[nodiscard]] const T& storage() const;
47
48 [[nodiscard]] int scale() const;
49
50 private:
51
52 template <class FLOAT_TYPE>
53 [[nodiscard]] FLOAT_TYPE convert_to_floating_point() const
54 requires(!is_int_placeholder_v<T>);
55
56
57 T m_value;
58 int m_scale;
59 };
60
61 template <typename T>
62 constexpr bool is_decimal_v = mpl::is_type_instance_of_v<T, decimal>;
63
64 template <typename T>
66
67 template <decimal_integer_type T>
69 requires(!is_int_placeholder_v<T>)
70 : m_value(0)
71 , m_scale()
72 {
73 }
74
75 template <decimal_integer_type T>
78 : m_value()
79 , m_scale()
80 {
81 }
82
83 template <decimal_integer_type T>
85 : m_value(value)
86 , m_scale(scale)
87 {
88 }
89
90 template <decimal_integer_type T>
91 bool decimal<T>::operator==(const decimal& other) const
92 {
93 return m_value == other.m_value && m_scale == other.m_scale;
94 }
95
96 template <decimal_integer_type T>
97 bool decimal<T>::operator!=(const decimal& other) const
98 {
99 return !(*this == other);
100 }
101
102 template <decimal_integer_type T>
104 requires(!is_int_placeholder_v<T>)
105 {
106 return convert_to_floating_point<float>();
107 }
108
109 template <decimal_integer_type T>
110 decimal<T>::operator double() const
111 requires(!is_int_placeholder_v<T>)
112 {
113 return convert_to_floating_point<double>();
114 }
115
116 template <decimal_integer_type T>
117 decimal<T>::operator long double() const
118 requires(!is_int_placeholder_v<T>)
119 {
120 return convert_to_floating_point<long double>();
121 }
122
123 template <decimal_integer_type T>
124 decimal<T>::operator std::string() const
125 requires(!is_int_placeholder_v<T>)
126 {
127 std::stringstream ss;
128 ss << m_value;
129 std::string result = ss.str();
130 if (m_scale == 0)
131 {
132 return result;
133 }
134 if (result[0] == '0')
135 {
136 return "0";
137 }
138 // remove - (we handle it later)
139 if (result[0] == '-')
140 {
141 result = result.substr(1);
142 }
143
144 if (m_scale > 0)
145 {
146 if (result.length() <= static_cast<std::size_t>(m_scale))
147 {
148 result.insert(
149 0,
150 std::string(static_cast<std::size_t>(m_scale) + 1 - result.length(), '0')
151 ); // Pad with leading zeros
152 }
153 std::size_t int_part_len = result.length() - static_cast<std::size_t>(m_scale);
154 std::string int_part = result.substr(0, int_part_len);
155 std::string frac_part = result.substr(int_part_len);
156 result = int_part + "." + frac_part;
157 }
158 else
159 {
160 result += std::string(static_cast<std::size_t>(-m_scale), '0'); // Append zeros
161 }
162 // handle negative values
163 if (m_value < 0)
164 {
165 result.insert(0, 1, '-');
166 }
167 return result;
168 }
169
170 template <decimal_integer_type T>
171 const T& decimal<T>::storage() const
172 {
173 return m_value;
174 }
175
176 template <decimal_integer_type T>
178 {
179 return m_scale;
180 }
181
182 template <decimal_integer_type T>
183 template <class FLOAT_TYPE>
184 FLOAT_TYPE decimal<T>::convert_to_floating_point() const
185 requires(!is_int_placeholder_v<T>)
186 {
187 using to_type = FLOAT_TYPE;
188 if constexpr (std::is_same_v<T, int256_t>)
189 {
190 // danger zone
191 auto val = static_cast<int128_t>(m_value);
192 return static_cast<to_type>(val) / static_cast<to_type>(std::pow(10, m_scale));
193 }
194 else
195 {
196 return static_cast<to_type>(m_value) / static_cast<to_type>(std::pow(10, m_scale));
197 }
198 }
199
200} // namespace sparrow
201
202#if defined(__cpp_lib_format)
203
204template <typename T>
205struct std::formatter<sparrow::decimal<T>>
206{
207 constexpr auto parse(std::format_parse_context& ctx)
208 {
209 return ctx.begin();
210 }
211
212 auto format(const sparrow::decimal<T>& d, std::format_context& ctx) const
213 {
214 return std::format_to(ctx.out(), "Decimal({}, {})", d.storage(), d.scale());
215 }
216};
217
218template <typename T>
219std::ostream& operator<<(std::ostream& os, const sparrow::decimal<T>& value)
220{
221 os << std::format("{}", value);
222 return os;
223}
224
225#endif
bool operator!=(const decimal &other) const
Definition decimal.hpp:97
bool operator==(const decimal &other) const
Definition decimal.hpp:91
decimal(T value, int scale)
Definition decimal.hpp:84
primesum::int128_t int128_t
Definition large_int.hpp:88
constexpr bool is_int_placeholder_v
Definition large_int.hpp:86
constexpr bool is_decimal_v
Definition decimal.hpp:62
std::ostream & operator<<(std::ostream &os, const sparrow::nullval_t &)
Definition nullable.hpp:900