Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
half_int.hpp
Go to the documentation of this file.
1
6#pragma once
7
10
11#include <charconv>
12#include <cmath>
13#include <compare>
14#include <concepts>
15#include <cstddef>
16#include <cstdint>
17#include <format>
18#include <functional>
19#include <ios>
20#include <iosfwd>
21#include <istream>
22#include <limits>
23#include <ostream>
24#include <stdexcept>
25#include <string>
26#include <string_view>
27#include <system_error>
28
29namespace uni20
30{
31
34template <std::signed_integral T> class basic_half_int {
35 public:
37 using value_type = T;
38
40 struct twice_tag
41 {};
42
44 constexpr basic_half_int() = default;
45
51 template <std::signed_integral U>
52 explicit(std::numeric_limits<T>::digits < std::numeric_limits<U>::digits) constexpr basic_half_int(
53 basic_half_int<U> other) noexcept(std::numeric_limits<T>::digits >= std::numeric_limits<U>::digits)
54 {
55 if constexpr (std::numeric_limits<T>::digits < std::numeric_limits<U>::digits)
56 {
57 if (other.twice() < static_cast<U>(std::numeric_limits<T>::min()) ||
58 other.twice() > static_cast<U>(std::numeric_limits<T>::max()))
59 {
60 throw std::overflow_error("basic_half_int conversion overflow");
61 }
62 }
63 twice_ = static_cast<T>(other.twice());
64 }
65
68 template <std::integral U> constexpr basic_half_int(U value) : twice_(checked_double(static_cast<T>(value))) {}
69
72 template <std::floating_point U> explicit basic_half_int(U value) : twice_(rounded_twice(value)) {}
73
77 constexpr basic_half_int(T twice_value, twice_tag tag) : twice_(twice_value) { static_cast<void>(tag); }
78
81 constexpr auto twice() const -> T { return twice_; }
82
85 constexpr auto is_integral() const -> bool { return (twice_ & T{1}) == 0; }
86
89 constexpr auto to_double() const -> double { return static_cast<double>(twice_) / 2.0; }
90
94 constexpr auto to_int() const -> T
95 {
96 if (!this->is_integral())
97 {
98 throw std::runtime_error("basic_half_int cannot be converted to an integer exactly");
99 }
100 return twice_ / T{2};
101 }
102
106 constexpr auto to_int_assert() const -> T
107 {
109 return twice_ / T{2};
110 }
111
115 constexpr auto operator+=(basic_half_int other) -> basic_half_int&
116 {
117 twice_ += other.twice_;
118 return *this;
119 }
120
124 constexpr auto operator-=(basic_half_int other) -> basic_half_int&
125 {
126 twice_ -= other.twice_;
127 return *this;
128 }
129
132 constexpr auto operator++() -> basic_half_int&
133 {
134 twice_ += T{2};
135 return *this;
136 }
137
140 constexpr auto operator--() -> basic_half_int&
141 {
142 twice_ -= T{2};
143 return *this;
144 }
145
148 constexpr auto operator++(int) -> basic_half_int
149 {
150 auto const old = *this;
151 ++(*this);
152 return old;
153 }
154
157 constexpr auto operator--(int) -> basic_half_int
158 {
159 auto const old = *this;
160 --(*this);
161 return old;
162 }
163
167 constexpr auto operator<=>(basic_half_int const& other) const = default;
168
172 static auto parse(std::string_view text) -> basic_half_int
173 {
174 if (text.empty())
175 {
176 throw std::runtime_error("basic_half_int parse failed: empty string");
177 }
178
179 auto const slash = text.find('/');
180 if (slash != std::string_view::npos)
181 {
182 auto const numerator = parse_integral<T>(text.substr(0, slash));
183 auto const denominator = text.substr(slash + 1);
184 if (denominator != "2")
185 {
186 throw std::runtime_error("basic_half_int parse failed: expected denominator 2");
187 }
188 return from_twice(numerator);
189 }
190
191 auto const dot = text.find('.');
192 if (dot == std::string_view::npos)
193 {
194 return basic_half_int(parse_integral<T>(text));
195 }
196
197 auto const whole = text.substr(0, dot);
198 auto const fractional = text.substr(dot + 1);
199 if (fractional.empty())
200 {
201 return basic_half_int(parse_integral<T>(whole));
202 }
203 if (fractional != "0" && fractional != "5")
204 {
205 throw std::runtime_error("basic_half_int parse failed: expected decimal .0 or .5");
206 }
207
208 auto const sign = (!whole.empty() && whole.front() == '-') ? T{-1} : T{1};
209 auto const twice_whole = checked_double(parse_integral<T>(whole));
210 auto const half = (fractional == "5") ? sign : T{0};
211 if (half < 0 && twice_whole == std::numeric_limits<T>::min())
212 {
213 throw std::overflow_error("basic_half_int doubled representation overflow");
214 }
215 return from_twice(twice_whole + half);
216 }
217
218 private:
222 static constexpr auto from_twice(T twice_value) -> basic_half_int
223 {
224 return basic_half_int(twice_value, twice_tag{});
225 }
226
230 static constexpr auto checked_double(T value) -> T
231 {
232 if (value > std::numeric_limits<T>::max() / T{2} || value < std::numeric_limits<T>::min() / T{2})
233 {
234 throw std::overflow_error("basic_half_int doubled representation overflow");
235 }
236 return value * T{2};
237 }
238
242 template <std::floating_point U> static auto rounded_twice(U value) -> T
243 {
244 auto const doubled = std::round(static_cast<long double>(value) * 2.0L);
245 if (doubled > static_cast<long double>(std::numeric_limits<T>::max()) ||
246 doubled < static_cast<long double>(std::numeric_limits<T>::min()))
247 {
248 throw std::overflow_error("basic_half_int rounded representation overflow");
249 }
250 return static_cast<T>(doubled);
251 }
252
257 template <std::integral Int> static auto parse_integral(std::string_view text) -> Int
258 {
259 if (text.empty())
260 {
261 throw std::runtime_error("basic_half_int parse failed: missing integer component");
262 }
263 auto const begin = text.data();
264 auto const end = text.data() + text.size();
265 Int value{};
266 auto const [ptr, ec] = std::from_chars(begin, end, value);
267 if (ec != std::errc{} || ptr != end)
268 {
269 throw std::runtime_error("basic_half_int parse failed: invalid integer component");
270 }
271 return value;
272 }
273
274 T twice_ = 0;
275};
276
282template <std::signed_integral T> struct numeric_limits<basic_half_int<T>>
283{
284 static constexpr bool is_specialized = true;
285 static constexpr bool is_signed = true;
286 static constexpr bool is_integer = false;
287 static constexpr bool is_exact = true;
288 static constexpr bool is_bounded = true;
289 static constexpr bool is_iec559 = false;
290 static constexpr int radix = 2;
291 static constexpr int digits() noexcept { return numeric_limits<T>::digits(); }
292 static constexpr int digits(basic_half_int<T> const&) noexcept { return digits(); }
293 static constexpr int digits(Precision p)
294 {
295 if (!p.is_exact()) throw std::invalid_argument("half-integer arithmetic requires exact precision");
296 return digits();
297 }
298 static constexpr bool has_infinity = false;
299 static constexpr bool has_quiet_NaN = false;
300 static constexpr bool has_signaling_NaN = false;
301
303 static constexpr basic_half_int<T> lowest() noexcept
304 {
306 }
307
309 static constexpr basic_half_int<T> max() noexcept
310 {
312 }
313};
314
317
322template <std::signed_integral T> constexpr auto from_twice(T twice_value) -> basic_half_int<T>
323{
324 return basic_half_int<T>(twice_value, typename basic_half_int<T>::twice_tag{});
325}
326
332template <std::signed_integral T>
333constexpr auto operator+(basic_half_int<T> lhs, basic_half_int<T> rhs) -> basic_half_int<T>
334{
335 lhs += rhs;
336 return lhs;
337}
338
344template <std::signed_integral T>
346{
347 lhs -= rhs;
348 return lhs;
349}
350
355template <std::signed_integral T> constexpr auto operator-(basic_half_int<T> value) -> basic_half_int<T>
356{
357 return from_twice<T>(-value.twice());
358}
359
366template <std::signed_integral T, std::integral U>
367constexpr auto operator*(basic_half_int<T> lhs, U rhs) -> basic_half_int<T>
368{
369 return from_twice<T>(lhs.twice() * static_cast<T>(rhs));
370}
371
378template <std::integral U, std::signed_integral T>
379constexpr auto operator*(U lhs, basic_half_int<T> rhs) -> basic_half_int<T>
380{
381 return rhs * lhs;
382}
383
390template <std::signed_integral T, std::floating_point U> constexpr auto operator*(basic_half_int<T> lhs, U rhs) -> U
391{
392 return static_cast<U>(lhs.to_double()) * rhs;
393}
394
401template <std::floating_point U, std::signed_integral T> constexpr auto operator*(U lhs, basic_half_int<T> rhs) -> U
402{
403 return lhs * static_cast<U>(rhs.to_double());
404}
405
411template <std::signed_integral T> constexpr auto operator*(basic_half_int<T> lhs, basic_half_int<T> rhs) -> double
412{
413 return lhs.to_double() * rhs.to_double();
414}
415
422template <std::signed_integral T, std::floating_point U> constexpr auto operator/(basic_half_int<T> lhs, U rhs) -> U
423{
424 return static_cast<U>(lhs.to_double()) / rhs;
425}
426
431template <std::signed_integral T> constexpr auto abs(basic_half_int<T> value) -> basic_half_int<T>
432{
433 return (value.twice() < 0) ? -value : value;
434}
435
440template <std::signed_integral T> constexpr auto is_integral(basic_half_int<T> value) -> bool
441{
442 return value.is_integral();
443}
444
449template <std::signed_integral T> constexpr auto to_int(basic_half_int<T> value) -> T { return value.to_int(); }
450
455template <std::signed_integral T> constexpr auto to_int_assert(basic_half_int<T> value) -> T
456{
457 return value.to_int_assert();
458}
459
464template <std::signed_integral T> inline auto to_string_fraction(basic_half_int<T> value) -> std::string
465{
466 if (value.is_integral())
467 {
468 return std::to_string(value.to_int_assert());
469 }
470 return std::to_string(value.twice()) + "/2";
471}
472
477template <std::signed_integral T> inline auto to_string(basic_half_int<T> value) -> std::string
478{
479 if (value.is_integral())
480 {
481 return std::to_string(value.to_int_assert());
482 }
483
484 auto const whole = value.twice() / T{2};
485 auto const fractional = (value.twice() < 0 && whole == 0) ? "-0.5" : std::to_string(whole) + ".5";
486 return fractional;
487}
488
493template <std::integral T> constexpr auto minus1pow(T value) -> int
494{
495 return 1 - static_cast<int>((value & T{1}) << 1);
496}
497
504template <std::signed_integral T>
506{
507 return (b >= abs(a - c)) && (b <= a + c) && (a + b + c).is_integral();
508}
509
515template <std::signed_integral T> inline auto operator<<(std::ostream& out, basic_half_int<T> value) -> std::ostream&
516{
517 out << to_string(value);
518 return out;
519}
520
526template <std::signed_integral T> inline auto operator>>(std::istream& in, basic_half_int<T>& value) -> std::istream&
527{
528 std::string token;
529 in >> token;
530 if (!in)
531 {
532 return in;
533 }
534
535 try
536 {
538 }
539 catch (std::runtime_error const&)
540 {
541 in.setstate(std::ios::failbit);
542 }
543 return in;
544}
545
546} // namespace uni20
547
548template <std::signed_integral T, typename CharT> struct std::formatter<uni20::basic_half_int<T>, CharT>
549{
550 constexpr auto parse(std::basic_format_parse_context<CharT>& ctx) { return ctx.begin(); }
551
552 template <typename FormatContext> auto format(uni20::basic_half_int<T> const& value, FormatContext& ctx) const
553 {
554 return std::format_to(ctx.out(), "{}", uni20::to_string(value));
555 }
556};
557
558template <std::signed_integral T> struct std::hash<uni20::basic_half_int<T>>
559{
563 auto operator()(uni20::basic_half_int<T> const& value) const noexcept -> std::size_t
564 {
565 return std::hash<T>{}(value.twice());
566 }
567};
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
Signed value type that stores multiples of one half exactly.
Definition half_int.hpp:34
constexpr auto operator-=(basic_half_int other) -> basic_half_int &
Subtract another half-integer value.
Definition half_int.hpp:124
constexpr basic_half_int(T twice_value, twice_tag tag)
Construct directly from the doubled representation.
Definition half_int.hpp:77
constexpr auto operator--(int) -> basic_half_int
Postfix decrement by one.
Definition half_int.hpp:157
constexpr auto operator+=(basic_half_int other) -> basic_half_int &
Add another half-integer value.
Definition half_int.hpp:115
constexpr auto is_integral() const -> bool
Return whether the represented value is integral.
Definition half_int.hpp:85
constexpr auto operator++() -> basic_half_int &
Prefix increment by one.
Definition half_int.hpp:132
constexpr auto operator<=>(basic_half_int const &other) const =default
Compare two half-integer values.
constexpr auto to_double() const -> double
Convert to double.
Definition half_int.hpp:89
constexpr auto operator++(int) -> basic_half_int
Postfix increment by one.
Definition half_int.hpp:148
T value_type
Underlying signed storage type.
Definition half_int.hpp:37
basic_half_int(U value)
Construct by rounding a floating-point value to the nearest half-integer.
Definition half_int.hpp:72
constexpr auto to_int_assert() const -> T
Convert to the underlying integral type assuming exact integrality.
Definition half_int.hpp:106
static auto parse(std::string_view text) -> basic_half_int
Parse a textual half-integer representation.
Definition half_int.hpp:172
constexpr auto twice() const -> T
Return the doubled representation.
Definition half_int.hpp:81
constexpr auto to_int() const -> T
Convert to the underlying integral type.
Definition half_int.hpp:94
constexpr basic_half_int()=default
Construct the value zero.
constexpr basic_half_int(U value)
Construct from an integral value.
Definition half_int.hpp:68
constexpr auto operator--() -> basic_half_int &
Prefix decrement by one.
Definition half_int.hpp:140
bool empty() const noexcept
Return whether this document has no spans.
Definition presentation.cpp:1182
locale_t value
Definition data_table.cpp:49
presentation::styled_text text
Definition display.cpp:141
Definition cli.cpp:6
Definition data_table.cpp:8
Definition async/async.hpp:26
constexpr auto minus1pow(T value) -> int
Return (-1)^x for an integral exponent.
Definition half_int.hpp:493
constexpr auto operator*(basic_half_int< T > lhs, U rhs) -> basic_half_int< T >
Multiply a half-integer value by an integral scalar.
Definition half_int.hpp:367
auto operator<<(std::ostream &out, basic_half_int< T > value) -> std::ostream &
Stream a half-integer in integer or .5 decimal form.
Definition half_int.hpp:515
constexpr auto abs(basic_half_int< T > value) -> basic_half_int< T >
Return the absolute value of a half-integer.
Definition half_int.hpp:431
constexpr auto operator-(basic_half_int< T > lhs, basic_half_int< T > rhs) -> basic_half_int< T >
Subtract two half-integer values.
Definition half_int.hpp:345
constexpr auto from_twice(T twice_value) -> basic_half_int< T >
Construct a half-integer from its doubled representation.
Definition half_int.hpp:322
constexpr auto to_int_assert(basic_half_int< T > value) -> T
Convert a half-integer to its integral value assuming exact integrality.
Definition half_int.hpp:455
constexpr auto is_triangle(basic_half_int< T > a, basic_half_int< T > b, basic_half_int< T > c) -> bool
Return whether three nonnegative half-integers satisfy the triangle condition.
Definition half_int.hpp:505
auto to_string_fraction(basic_half_int< T > value) -> std::string
Format a half-integer as an integer or n/2.
Definition half_int.hpp:464
constexpr bool is_exact(detail::standard_complex< T > const &x)
A standard complex value is exact only when both components are exact.
Definition math.hpp:50
constexpr auto to_int(basic_half_int< T > value) -> T
Convert a half-integer to its integral value.
Definition half_int.hpp:449
auto to_string(basic_half_int< T > value) -> std::string
Format a half-integer as an integer or decimal .5.
Definition half_int.hpp:477
constexpr auto is_integral(basic_half_int< T > value) -> bool
Return whether a half-integer value is integral.
Definition half_int.hpp:440
auto operator>>(std::istream &in, basic_half_int< T > &value) -> std::istream &
Parse a half-integer from integer, .0, .5, or n/2 form.
Definition half_int.hpp:526
constexpr auto operator/(basic_half_int< T > lhs, U rhs) -> U
Divide a half-integer value by a floating-point scalar.
Definition half_int.hpp:422
constexpr auto parse(std::basic_format_parse_context< CharT > &ctx)
Definition half_int.hpp:550
auto format(uni20::basic_half_int< T > const &value, FormatContext &ctx) const
Definition half_int.hpp:552
auto operator()(uni20::basic_half_int< T > const &value) const noexcept -> std::size_t
Hash a half-integer by its doubled representation.
Definition half_int.hpp:563
Tag type used to construct directly from the doubled representation.
Definition half_int.hpp:41
static constexpr basic_half_int< T > max() noexcept
Greatest representable half-integer.
Definition half_int.hpp:309
static constexpr int digits() noexcept
Definition half_int.hpp:291
static constexpr int digits(Precision p)
Definition half_int.hpp:293
static constexpr basic_half_int< T > lowest() noexcept
Most negative representable half-integer.
Definition half_int.hpp:303
static constexpr int digits(basic_half_int< T > const &) noexcept
Definition half_int.hpp:292
Project-level numeric limits customization point.
Definition numeric_limits.hpp:16
#define DEBUG_PRECONDITION(cond,...)
Definition trace.hpp:648