Sourcemeta Core 0.0.0
Loading...
Searching...
No Matches
numeric_decimal.h
1#ifndef SOURCEMETA_CORE_NUMERIC_DECIMAL_H_
2#define SOURCEMETA_CORE_NUMERIC_DECIMAL_H_
3
4#ifndef SOURCEMETA_CORE_NUMERIC_EXPORT
5#include <sourcemeta/core/numeric_export.h>
6#endif
7
8#include <sourcemeta/core/preprocessor.h>
9
10#include <cassert> // assert
11#include <concepts> // std::integral
12#include <cstdint> // std::int32_t, std::int64_t, std::uint32_t, std::uint64_t
13#include <string> // std::string
14#include <string_view> // std::string_view
15#include <type_traits> // std::is_signed_v
16
17namespace sourcemeta::core {
18
21class SOURCEMETA_CORE_NUMERIC_EXPORT Decimal {
22public:
24 Decimal() noexcept;
25
28
30 Decimal(const Decimal &other);
31
33 Decimal(Decimal &&other) noexcept;
34
36 template <typename T>
37 requires std::integral<T> && std::is_signed_v<T> &&
38 (!std::same_as<T, std::int64_t>)
39 Decimal(const T value) : Decimal{static_cast<std::int64_t>(value)} {}
40
42 Decimal(std::int64_t value);
43
45 template <typename T>
46 requires std::integral<T> && std::is_unsigned_v<T> &&
47 (!std::same_as<T, std::uint64_t>)
48 Decimal(const T value) : Decimal{static_cast<std::uint64_t>(value)} {}
49
51 Decimal(std::uint64_t value);
52
54 explicit Decimal(float value);
55
57 explicit Decimal(double value);
58
60 explicit Decimal(const char *const value);
61
63 explicit Decimal(const std::string &value);
64
66 explicit Decimal(const std::string_view value);
67
69 auto operator=(const Decimal &other) -> Decimal &;
70
72 auto operator=(Decimal &&other) noexcept -> Decimal &;
73
75 [[nodiscard]] static auto nan(std::uint64_t payload = 0) -> Decimal;
76
78 [[nodiscard]] static auto snan(std::uint64_t payload = 0) -> Decimal;
79
82 [[nodiscard]] static auto strict_from(double value) -> Decimal;
83
86 [[nodiscard]] static auto exact_from(double value) -> Decimal;
87
89 [[nodiscard]] static auto infinity() -> Decimal;
90
92 [[nodiscard]] static auto negative_infinity() -> Decimal;
93
95 [[nodiscard]] auto to_scientific_string() const -> std::string;
96
98 [[nodiscard]] auto to_string() const -> std::string;
99
101 [[nodiscard]] auto to_int64() const -> std::int64_t;
102
104 [[nodiscard]] auto to_int32() const -> std::int32_t;
105
107 [[nodiscard]] auto to_uint64() const -> std::uint64_t;
108
110 [[nodiscard]] auto to_uint32() const -> std::uint32_t;
111
113 [[nodiscard]] auto to_float() const -> float;
114
116 [[nodiscard]] auto to_double() const -> double;
117
119 [[nodiscard]] auto is_zero() const -> bool;
120
125 [[nodiscard]] auto is_integral() const -> bool;
126
134 [[nodiscard]] SOURCEMETA_FORCEINLINE auto is_integer() const -> bool {
135 return (this->flags_ & FLAG_INTEGER_LITERAL) != 0;
136 }
137
139 [[nodiscard]] SOURCEMETA_FORCEINLINE auto is_finite() const -> bool {
140 return (this->flags_ & (FLAG_NAN | FLAG_SNAN | FLAG_INFINITE)) == 0;
141 }
142
144 [[nodiscard]] SOURCEMETA_FORCEINLINE auto is_real() const -> bool {
145 return this->is_finite() && !this->is_integral();
146 }
147
150 [[nodiscard]] auto is_float() const -> bool;
151
154 [[nodiscard]] auto is_double() const -> bool;
155
157 [[nodiscard]] auto is_int32() const -> bool;
158
160 [[nodiscard]] auto is_int64() const -> bool;
161
163 [[nodiscard]] auto is_uint32() const -> bool;
164
166 [[nodiscard]] auto is_uint64() const -> bool;
167
170 [[nodiscard]] SOURCEMETA_FORCEINLINE auto is_nan() const -> bool {
171 return (this->flags_ & FLAG_NAN) != 0;
172 }
173
175 [[nodiscard]] SOURCEMETA_FORCEINLINE auto is_snan() const -> bool {
176 return (this->flags_ & FLAG_SNAN) != 0;
177 }
178
180 [[nodiscard]] SOURCEMETA_FORCEINLINE auto is_qnan() const -> bool {
181 return (this->flags_ & FLAG_NAN) != 0 && ((this->flags_ & FLAG_SNAN) == 0);
182 }
183
185 [[nodiscard]] SOURCEMETA_FORCEINLINE auto nan_payload() const
186 -> std::uint64_t {
187 assert(this->is_nan());
188 return static_cast<std::uint64_t>(this->coefficient_);
189 }
190
192 [[nodiscard]] SOURCEMETA_FORCEINLINE auto is_infinite() const -> bool {
193 return (this->flags_ & FLAG_INFINITE) != 0;
194 }
195
197 [[nodiscard]] SOURCEMETA_FORCEINLINE auto is_signed() const -> bool {
198 return (this->flags_ & FLAG_SIGN) != 0;
199 }
200
202 [[nodiscard]] auto to_integral() const -> Decimal;
203
205 [[nodiscard]] auto divisible_by(const Decimal &divisor) const -> bool;
206
208 [[nodiscard]] auto reduce() const -> Decimal;
209
212 [[nodiscard]] auto trim() const -> Decimal;
213
215 [[nodiscard]] auto logb() const -> Decimal;
216
218 [[nodiscard]] auto scale_by(const Decimal &scale) const -> Decimal;
219
221 [[nodiscard]] auto same_quantum(const Decimal &other) const -> bool;
222
224 [[nodiscard]] auto compare_total(const Decimal &other) const -> Decimal;
225
227 [[nodiscard]] auto divide_integer(const Decimal &other) const -> Decimal;
228
230 auto operator+=(const Decimal &other) -> Decimal &;
231
233 auto operator-=(const Decimal &other) -> Decimal &;
234
236 auto operator*=(const Decimal &other) -> Decimal &;
237
239 auto operator/=(const Decimal &other) -> Decimal &;
240
242 auto operator%=(const Decimal &other) -> Decimal &;
243
245 [[nodiscard]] auto operator+(const Decimal &other) const -> Decimal;
246
248 [[nodiscard]] auto operator-(const Decimal &other) const -> Decimal;
249
251 [[nodiscard]] auto operator*(const Decimal &other) const -> Decimal;
252
254 [[nodiscard]] auto operator/(const Decimal &other) const -> Decimal;
255
257 [[nodiscard]] auto operator%(const Decimal &other) const -> Decimal;
258
260 [[nodiscard]] auto operator-() const -> Decimal;
261
263 [[nodiscard]] auto operator+() const -> Decimal;
264
266 auto operator++() -> Decimal &;
267
269 auto operator++(int) -> Decimal;
270
272 auto operator--() -> Decimal &;
273
275 auto operator--(int) -> Decimal;
276
278 [[nodiscard]] auto operator==(const Decimal &other) const -> bool;
279
281 [[nodiscard]] auto operator!=(const Decimal &other) const -> bool;
282
284 [[nodiscard]] auto operator<(const Decimal &other) const -> bool;
285
287 [[nodiscard]] auto operator<=(const Decimal &other) const -> bool;
288
290 [[nodiscard]] auto operator>(const Decimal &other) const -> bool;
291
293 [[nodiscard]] auto operator>=(const Decimal &other) const -> bool;
294
295private:
296 static constexpr std::uint8_t FLAG_SIGN = 0x01;
297 static constexpr std::uint8_t FLAG_NAN = 0x02;
298 static constexpr std::uint8_t FLAG_SNAN = 0x04;
299 static constexpr std::uint8_t FLAG_INFINITE = 0x08;
300 static constexpr std::uint8_t FLAG_INTEGER_LITERAL = 0x40;
301
302 // Gives up at most the stated number of trailing zeros, lifting the exponent
303 // by one for each, which is what both of the public strips are made of
304 [[nodiscard]] auto remove_trailing_zeros(std::int32_t allowance) const
305 -> Decimal;
306
307 std::int64_t coefficient_{0};
308 std::uint64_t coefficient_high_{0};
309 std::int32_t exponent_{0};
310 std::uint8_t flags_{0};
311};
312
313template <typename T>
314 requires std::integral<T>
315inline auto operator+(const T left, const Decimal &right) -> Decimal {
316 return Decimal{left} + right;
317}
318
319template <typename T>
320 requires std::integral<T>
321inline auto operator-(const T left, const Decimal &right) -> Decimal {
322 return Decimal{left} - right;
323}
324
325template <typename T>
326 requires std::integral<T>
327inline auto operator*(const T left, const Decimal &right) -> Decimal {
328 return Decimal{left} * right;
329}
330
331template <typename T>
332 requires std::integral<T>
333inline auto operator/(const T left, const Decimal &right) -> Decimal {
334 return Decimal{left} / right;
335}
336
337template <typename T>
338 requires std::integral<T>
339inline auto operator%(const T left, const Decimal &right) -> Decimal {
340 return Decimal{left} % right;
341}
342
343template <typename T>
344 requires std::integral<T>
345inline auto operator==(const T left, const Decimal &right) -> bool {
346 return Decimal{left} == right;
347}
348
349template <typename T>
350 requires std::integral<T>
351inline auto operator!=(const T left, const Decimal &right) -> bool {
352 return Decimal{left} != right;
353}
354
355template <typename T>
356 requires std::integral<T>
357inline auto operator<(const T left, const Decimal &right) -> bool {
358 return Decimal{left} < right;
359}
360
361template <typename T>
362 requires std::integral<T>
363inline auto operator<=(const T left, const Decimal &right) -> bool {
364 return Decimal{left} <= right;
365}
366
367template <typename T>
368 requires std::integral<T>
369inline auto operator>(const T left, const Decimal &right) -> bool {
370 return Decimal{left} > right;
371}
372
373template <typename T>
374 requires std::integral<T>
375inline auto operator>=(const T left, const Decimal &right) -> bool {
376 return Decimal{left} >= right;
377}
378
379} // namespace sourcemeta::core
380
381#endif
auto is_double() const -> bool
Decimal(const T value)
Construct a decimal number from an unsigned integral type.
Definition numeric_decimal.h:48
SOURCEMETA_FORCEINLINE auto is_snan() const -> bool
Check if the decimal number is a signaling NaN.
Definition numeric_decimal.h:175
auto to_double() const -> double
Convert the decimal number to a 64-bit double.
Decimal(const std::string &value)
Construct a decimal number from a C++ string.
Decimal(const char *const value)
Construct a decimal number from a C-string.
auto divide_integer(const Decimal &other) const -> Decimal
Integer division (truncate toward zero).
Decimal(float value)
Construct a decimal number from a 32-bit float.
auto to_uint32() const -> std::uint32_t
Convert the decimal number to a 32-bit unsigned integer.
auto is_uint64() const -> bool
Check if the decimal number fits in a 64-bit unsigned integer.
auto is_uint32() const -> bool
Check if the decimal number fits in a 32-bit unsigned integer.
auto to_string() const -> std::string
Convert the decimal number to a plain string representation.
auto reduce() const -> Decimal
Strip trailing zeros from the coefficient.
SOURCEMETA_FORCEINLINE auto is_signed() const -> bool
Check if the decimal number is signed (negative, including -0).
Definition numeric_decimal.h:197
auto is_int32() const -> bool
Check if the decimal number fits in a 32-bit signed integer.
SOURCEMETA_FORCEINLINE auto is_finite() const -> bool
Check if the decimal number is finite.
Definition numeric_decimal.h:139
SOURCEMETA_FORCEINLINE auto is_real() const -> bool
Check if the decimal number is a real number (finite and not NaN).
Definition numeric_decimal.h:144
SOURCEMETA_FORCEINLINE auto is_qnan() const -> bool
Check if the decimal number is a quiet NaN.
Definition numeric_decimal.h:180
auto is_integral() const -> bool
auto is_zero() const -> bool
Check if the decimal number is zero.
SOURCEMETA_FORCEINLINE auto is_integer() const -> bool
Definition numeric_decimal.h:134
auto to_scientific_string() const -> std::string
Convert the decimal number to scientific notation string.
auto is_float() const -> bool
Decimal(const std::string_view value)
Construct a decimal number from a string view.
auto logb() const -> Decimal
Return the adjusted exponent (floor of base-10 logarithm).
static auto exact_from(double value) -> Decimal
auto divisible_by(const Decimal &divisor) const -> bool
Check if this decimal number is divisible by another.
Decimal() noexcept
Construct a decimal number initialized to zero.
static auto nan(std::uint64_t payload=0) -> Decimal
Create a quiet NaN (Not a Number) value with an optional payload.
auto to_uint64() const -> std::uint64_t
Convert the decimal number to a 64-bit unsigned integer.
static auto strict_from(double value) -> Decimal
auto same_quantum(const Decimal &other) const -> bool
Check if two numbers have the same quantum (exponent).
SOURCEMETA_FORCEINLINE auto is_nan() const -> bool
Definition numeric_decimal.h:170
SOURCEMETA_FORCEINLINE auto nan_payload() const -> std::uint64_t
Get the payload of a NaN value (0 if no payload).
Definition numeric_decimal.h:185
Decimal(std::int64_t value)
Construct a decimal number from a 64-bit signed integer.
Decimal(double value)
Construct a decimal number from a 64-bit double.
auto to_integral() const -> Decimal
Round the decimal number to an integral value.
auto to_int32() const -> std::int32_t
Convert the decimal number to a 32-bit signed integer.
Decimal(std::uint64_t value)
Construct a decimal number from a 64-bit unsigned integer.
auto compare_total(const Decimal &other) const -> Decimal
IEEE 754 total ordering comparison returning -1, 0, or 1.
static auto negative_infinity() -> Decimal
Create a negative infinity value.
SOURCEMETA_FORCEINLINE auto is_infinite() const -> bool
Check if the decimal number is infinite.
Definition numeric_decimal.h:192
auto trim() const -> Decimal
auto scale_by(const Decimal &scale) const -> Decimal
Scale the number by a power of 10.
static auto infinity() -> Decimal
Create a positive infinity value.
auto to_int64() const -> std::int64_t
Convert the decimal number to a 64-bit signed integer.
static auto snan(std::uint64_t payload=0) -> Decimal
Create a signaling NaN value with an optional payload.
auto to_float() const -> float
Convert the decimal number to a 32-bit float.
auto is_int64() const -> bool
Check if the decimal number fits in a 64-bit signed integer.
Definition numeric_decimal.h:21