1 // -*- C++ -*- operator<=> three-way comparison support. 2 3 // Copyright (C) 2019-2024 Free Software Foundation, Inc. 4 // 5 // This file is part of GCC. 6 // 7 // GCC is free software; you can redistribute it and/or modify 8 // it under the terms of the GNU General Public License as published by 9 // the Free Software Foundation; either version 3, or (at your option) 10 // any later version. 11 // 12 // GCC is distributed in the hope that it will be useful, 13 // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 // GNU General Public License for more details. 16 // 17 // Under Section 7 of GPL version 3, you are granted additional 18 // permissions described in the GCC Runtime Library Exception, version 19 // 3.1, as published by the Free Software Foundation. 20 21 // You should have received a copy of the GNU General Public License and 22 // a copy of the GCC Runtime Library Exception along with this program; 23 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 24 // <http://www.gnu.org/licenses/>. 25 26 /** @file compare 27 * This is a Standard C++ Library header. 28 */ 29 30 #ifndef _COMPARE 31 #define _COMPARE 32 33 #pragma GCC system_header 34 35 #define __glibcxx_want_three_way_comparison 36 #include <bits/version.h> 37 38 #if __cplusplus > 201703L && __cpp_impl_three_way_comparison >= 201907L 39 40 #include <concepts> 41 42 namespace std _GLIBCXX_VISIBILITY(default) 43 { 44 // [cmp.categories], comparison category types 45 46 namespace __cmp_cat 47 { 48 using type = signed char; 49 50 enum class _Ord : type { equivalent = 0, less = -1, greater = 1 }; 51 52 enum class _Ncmp : type { _Unordered = 2 }; 53 54 struct __unspec 55 { 56 consteval __unspec(__unspec*) noexcept { } 57 }; 58 } 59 60 class partial_ordering 61 { 62 // less=0xff, equiv=0x00, greater=0x01, unordered=0x02 63 __cmp_cat::type _M_value; 64 65 constexpr explicit 66 partial_ordering(__cmp_cat::_Ord __v) noexcept 67 : _M_value(__cmp_cat::type(__v)) 68 { } 69 70 constexpr explicit 71 partial_ordering(__cmp_cat::_Ncmp __v) noexcept 72 : _M_value(__cmp_cat::type(__v)) 73 { } 74 75 friend class weak_ordering; 76 friend class strong_ordering; 77 78 public: 79 // valid values 80 static const partial_ordering less; 81 static const partial_ordering equivalent; 82 static const partial_ordering greater; 83 static const partial_ordering unordered; 84 85 // comparisons 86 [[nodiscard]] 87 friend constexpr bool 88 operator==(partial_ordering __v, __cmp_cat::__unspec) noexcept 89 { return __v._M_value == 0; } 90 91 [[nodiscard]] 92 friend constexpr bool 93 operator==(partial_ordering, partial_ordering) noexcept = default; 94 95 [[nodiscard]] 96 friend constexpr bool 97 operator< (partial_ordering __v, __cmp_cat::__unspec) noexcept 98 { return __v._M_value == -1; } 99 100 [[nodiscard]] 101 friend constexpr bool 102 operator> (partial_ordering __v, __cmp_cat::__unspec) noexcept 103 { return __v._M_value == 1; } 104 105 [[nodiscard]] 106 friend constexpr bool 107 operator<=(partial_ordering __v, __cmp_cat::__unspec) noexcept 108 { return __v._M_value <= 0; } 109 110 [[nodiscard]] 111 friend constexpr bool 112 operator>=(partial_ordering __v, __cmp_cat::__unspec) noexcept 113 { return __cmp_cat::type(__v._M_value & 1) == __v._M_value; } 114 115 [[nodiscard]] 116 friend constexpr bool 117 operator< (__cmp_cat::__unspec, partial_ordering __v) noexcept 118 { return __v._M_value == 1; } 119 120 [[nodiscard]] 121 friend constexpr bool 122 operator> (__cmp_cat::__unspec, partial_ordering __v) noexcept 123 { return __v._M_value == -1; } 124 125 [[nodiscard]] 126 friend constexpr bool 127 operator<=(__cmp_cat::__unspec, partial_ordering __v) noexcept 128 { return __cmp_cat::type(__v._M_value & 1) == __v._M_value; } 129 130 [[nodiscard]] 131 friend constexpr bool 132 operator>=(__cmp_cat::__unspec, partial_ordering __v) noexcept 133 { return 0 >= __v._M_value; } 134 135 [[nodiscard]] 136 friend constexpr partial_ordering 137 operator<=>(partial_ordering __v, __cmp_cat::__unspec) noexcept 138 { return __v; } 139 140 [[nodiscard]] 141 friend constexpr partial_ordering 142 operator<=>(__cmp_cat::__unspec, partial_ordering __v) noexcept 143 { 144 if (__v._M_value & 1) 145 return partial_ordering(__cmp_cat::_Ord(-__v._M_value)); 146 else 147 return __v; 148 } 149 }; 150 151 // valid values' definitions 152 inline constexpr partial_ordering 153 partial_ordering::less(__cmp_cat::_Ord::less); 154 155 inline constexpr partial_ordering 156 partial_ordering::equivalent(__cmp_cat::_Ord::equivalent); 157 158 inline constexpr partial_ordering 159 partial_ordering::greater(__cmp_cat::_Ord::greater); 160 161 inline constexpr partial_ordering 162 partial_ordering::unordered(__cmp_cat::_Ncmp::_Unordered); 163 164 class weak_ordering 165 { 166 __cmp_cat::type _M_value; 167 168 constexpr explicit 169 weak_ordering(__cmp_cat::_Ord __v) noexcept : _M_value(__cmp_cat::type(__v)) 170 { } 171 172 friend class strong_ordering; 173 174 public: 175 // valid values 176 static const weak_ordering less; 177 static const weak_ordering equivalent; 178 static const weak_ordering greater; 179 180 [[nodiscard]] 181 constexpr operator partial_ordering() const noexcept 182 { return partial_ordering(__cmp_cat::_Ord(_M_value)); } 183 184 // comparisons 185 [[nodiscard]] 186 friend constexpr bool 187 operator==(weak_ordering __v, __cmp_cat::__unspec) noexcept 188 { return __v._M_value == 0; } 189 190 [[nodiscard]] 191 friend constexpr bool 192 operator==(weak_ordering, weak_ordering) noexcept = default; 193 194 [[nodiscard]] 195 friend constexpr bool 196 operator< (weak_ordering __v, __cmp_cat::__unspec) noexcept 197 { return __v._M_value < 0; } 198 199 [[nodiscard]] 200 friend constexpr bool 201 operator> (weak_ordering __v, __cmp_cat::__unspec) noexcept 202 { return __v._M_value > 0; } 203 204 [[nodiscard]] 205 friend constexpr bool 206 operator<=(weak_ordering __v, __cmp_cat::__unspec) noexcept 207 { return __v._M_value <= 0; } 208 209 [[nodiscard]] 210 friend constexpr bool 211 operator>=(weak_ordering __v, __cmp_cat::__unspec) noexcept 212 { return __v._M_value >= 0; } 213 214 [[nodiscard]] 215 friend constexpr bool 216 operator< (__cmp_cat::__unspec, weak_ordering __v) noexcept 217 { return 0 < __v._M_value; } 218 219 [[nodiscard]] 220 friend constexpr bool 221 operator> (__cmp_cat::__unspec, weak_ordering __v) noexcept 222 { return 0 > __v._M_value; } 223 224 [[nodiscard]] 225 friend constexpr bool 226 operator<=(__cmp_cat::__unspec, weak_ordering __v) noexcept 227 { return 0 <= __v._M_value; } 228 229 [[nodiscard]] 230 friend constexpr bool 231 operator>=(__cmp_cat::__unspec, weak_ordering __v) noexcept 232 { return 0 >= __v._M_value; } 233 234 [[nodiscard]] 235 friend constexpr weak_ordering 236 operator<=>(weak_ordering __v, __cmp_cat::__unspec) noexcept 237 { return __v; } 238 239 [[nodiscard]] 240 friend constexpr weak_ordering 241 operator<=>(__cmp_cat::__unspec, weak_ordering __v) noexcept 242 { return weak_ordering(__cmp_cat::_Ord(-__v._M_value)); } 243 }; 244 245 // valid values' definitions 246 inline constexpr weak_ordering 247 weak_ordering::less(__cmp_cat::_Ord::less); 248 249 inline constexpr weak_ordering 250 weak_ordering::equivalent(__cmp_cat::_Ord::equivalent); 251 252 inline constexpr weak_ordering 253 weak_ordering::greater(__cmp_cat::_Ord::greater); 254 255 class strong_ordering 256 { 257 __cmp_cat::type _M_value; 258 259 constexpr explicit 260 strong_ordering(__cmp_cat::_Ord __v) noexcept 261 : _M_value(__cmp_cat::type(__v)) 262 { } 263 264 public: 265 // valid values 266 static const strong_ordering less; 267 static const strong_ordering equal; 268 static const strong_ordering equivalent; 269 static const strong_ordering greater; 270 271 [[nodiscard]] 272 constexpr operator partial_ordering() const noexcept 273 { return partial_ordering(__cmp_cat::_Ord(_M_value)); } 274 275 [[nodiscard]] 276 constexpr operator weak_ordering() const noexcept 277 { return weak_ordering(__cmp_cat::_Ord(_M_value)); } 278 279 // comparisons 280 [[nodiscard]] 281 friend constexpr bool 282 operator==(strong_ordering __v, __cmp_cat::__unspec) noexcept 283 { return __v._M_value == 0; } 284 285 [[nodiscard]] 286 friend constexpr bool 287 operator==(strong_ordering, strong_ordering) noexcept = default; 288 289 [[nodiscard]] 290 friend constexpr bool 291 operator< (strong_ordering __v, __cmp_cat::__unspec) noexcept 292 { return __v._M_value < 0; } 293 294 [[nodiscard]] 295 friend constexpr bool 296 operator> (strong_ordering __v, __cmp_cat::__unspec) noexcept 297 { return __v._M_value > 0; } 298 299 [[nodiscard]] 300 friend constexpr bool 301 operator<=(strong_ordering __v, __cmp_cat::__unspec) noexcept 302 { return __v._M_value <= 0; } 303 304 [[nodiscard]] 305 friend constexpr bool 306 operator>=(strong_ordering __v, __cmp_cat::__unspec) noexcept 307 { return __v._M_value >= 0; } 308 309 [[nodiscard]] 310 friend constexpr bool 311 operator< (__cmp_cat::__unspec, strong_ordering __v) noexcept 312 { return 0 < __v._M_value; } 313 314 [[nodiscard]] 315 friend constexpr bool 316 operator> (__cmp_cat::__unspec, strong_ordering __v) noexcept 317 { return 0 > __v._M_value; } 318 319 [[nodiscard]] 320 friend constexpr bool 321 operator<=(__cmp_cat::__unspec, strong_ordering __v) noexcept 322 { return 0 <= __v._M_value; } 323 324 [[nodiscard]] 325 friend constexpr bool 326 operator>=(__cmp_cat::__unspec, strong_ordering __v) noexcept 327 { return 0 >= __v._M_value; } 328 329 [[nodiscard]] 330 friend constexpr strong_ordering 331 operator<=>(strong_ordering __v, __cmp_cat::__unspec) noexcept 332 { return __v; } 333 334 [[nodiscard]] 335 friend constexpr strong_ordering 336 operator<=>(__cmp_cat::__unspec, strong_ordering __v) noexcept 337 { return strong_ordering(__cmp_cat::_Ord(-__v._M_value)); } 338 }; 339 340 // valid values' definitions 341 inline constexpr strong_ordering 342 strong_ordering::less(__cmp_cat::_Ord::less); 343 344 inline constexpr strong_ordering 345 strong_ordering::equal(__cmp_cat::_Ord::equivalent); 346 347 inline constexpr strong_ordering 348 strong_ordering::equivalent(__cmp_cat::_Ord::equivalent); 349 350 inline constexpr strong_ordering 351 strong_ordering::greater(__cmp_cat::_Ord::greater); 352 353 354 // named comparison functions 355 [[nodiscard]] 356 constexpr bool 357 is_eq(partial_ordering __cmp) noexcept 358 { return __cmp == 0; } 359 360 [[nodiscard]] 361 constexpr bool 362 is_neq(partial_ordering __cmp) noexcept 363 { return __cmp != 0; } 364 365 [[nodiscard]] 366 constexpr bool 367 is_lt (partial_ordering __cmp) noexcept 368 { return __cmp < 0; } 369 370 [[nodiscard]] 371 constexpr bool 372 is_lteq(partial_ordering __cmp) noexcept 373 { return __cmp <= 0; } 374 375 [[nodiscard]] 376 constexpr bool 377 is_gt (partial_ordering __cmp) noexcept 378 { return __cmp > 0; } 379 380 [[nodiscard]] 381 constexpr bool 382 is_gteq(partial_ordering __cmp) noexcept 383 { return __cmp >= 0; } 384 385 namespace __detail 386 { 387 template<typename _Tp> 388 inline constexpr unsigned __cmp_cat_id = 1; 389 template<> 390 inline constexpr unsigned __cmp_cat_id<partial_ordering> = 2; 391 template<> 392 inline constexpr unsigned __cmp_cat_id<weak_ordering> = 4; 393 template<> 394 inline constexpr unsigned __cmp_cat_id<strong_ordering> = 8; 395 396 template<typename... _Ts> 397 constexpr auto __common_cmp_cat() 398 { 399 constexpr unsigned __cats = (__cmp_cat_id<_Ts> | ...); 400 // If any Ti is not a comparison category type, U is void. 401 if constexpr (__cats & 1) 402 return; 403 // Otherwise, if at least one Ti is std::partial_ordering, 404 // U is std::partial_ordering. 405 else if constexpr (bool(__cats & __cmp_cat_id<partial_ordering>)) 406 return partial_ordering::equivalent; 407 // Otherwise, if at least one Ti is std::weak_ordering, 408 // U is std::weak_ordering. 409 else if constexpr (bool(__cats & __cmp_cat_id<weak_ordering>)) 410 return weak_ordering::equivalent; 411 // Otherwise, U is std::strong_ordering. 412 else 413 return strong_ordering::equivalent; 414 } 415 } // namespace __detail 416 417 // [cmp.common], common comparison category type 418 template<typename... _Ts> 419 struct common_comparison_category 420 { 421 using type = decltype(__detail::__common_cmp_cat<_Ts...>()); 422 }; 423 424 // Partial specializations for one and zero argument cases. 425 426 template<typename _Tp> 427 struct common_comparison_category<_Tp> 428 { using type = void; }; 429 430 template<> 431 struct common_comparison_category<partial_ordering> 432 { using type = partial_ordering; }; 433 434 template<> 435 struct common_comparison_category<weak_ordering> 436 { using type = weak_ordering; }; 437 438 template<> 439 struct common_comparison_category<strong_ordering> 440 { using type = strong_ordering; }; 441 442 template<> 443 struct common_comparison_category<> 444 { using type = strong_ordering; }; 445 446 template<typename... _Ts> 447 using common_comparison_category_t 448 = typename common_comparison_category<_Ts...>::type; 449 450 #if __cpp_lib_three_way_comparison >= 201907L 451 // C++ >= 20 && impl_3way_comparison >= 201907 && lib_concepts 452 namespace __detail 453 { 454 template<typename _Tp, typename _Cat> 455 concept __compares_as 456 = same_as<common_comparison_category_t<_Tp, _Cat>, _Cat>; 457 } // namespace __detail 458 459 // [cmp.concept], concept three_way_comparable 460 template<typename _Tp, typename _Cat = partial_ordering> 461 concept three_way_comparable 462 = __detail::__weakly_eq_cmp_with<_Tp, _Tp> 463 && __detail::__partially_ordered_with<_Tp, _Tp> 464 && requires(const remove_reference_t<_Tp>& __a, 465 const remove_reference_t<_Tp>& __b) 466 { 467 { __a <=> __b } -> __detail::__compares_as<_Cat>; 468 }; 469 470 template<typename _Tp, typename _Up, typename _Cat = partial_ordering> 471 concept three_way_comparable_with 472 = three_way_comparable<_Tp, _Cat> 473 && three_way_comparable<_Up, _Cat> 474 && common_reference_with<const remove_reference_t<_Tp>&, 475 const remove_reference_t<_Up>&> 476 && three_way_comparable< 477 common_reference_t<const remove_reference_t<_Tp>&, 478 const remove_reference_t<_Up>&>, _Cat> 479 && __detail::__weakly_eq_cmp_with<_Tp, _Up> 480 && __detail::__partially_ordered_with<_Tp, _Up> 481 && requires(const remove_reference_t<_Tp>& __t, 482 const remove_reference_t<_Up>& __u) 483 { 484 { __t <=> __u } -> __detail::__compares_as<_Cat>; 485 { __u <=> __t } -> __detail::__compares_as<_Cat>; 486 }; 487 488 namespace __detail 489 { 490 template<typename _Tp, typename _Up> 491 using __cmp3way_res_t 492 = decltype(std::declval<_Tp>() <=> std::declval<_Up>()); 493 494 // Implementation of std::compare_three_way_result. 495 // It is undefined for a program to add specializations of 496 // std::compare_three_way_result, so the std::compare_three_way_result_t 497 // alias ignores std::compare_three_way_result and uses 498 // __detail::__cmp3way_res_impl directly instead. 499 template<typename _Tp, typename _Up> 500 struct __cmp3way_res_impl 501 { }; 502 503 template<typename _Tp, typename _Up> 504 requires requires { typename __cmp3way_res_t<__cref<_Tp>, __cref<_Up>>; } 505 struct __cmp3way_res_impl<_Tp, _Up> 506 { 507 using type = __cmp3way_res_t<__cref<_Tp>, __cref<_Up>>; 508 }; 509 } // namespace __detail 510 511 /// [cmp.result], result of three-way comparison 512 template<typename _Tp, typename _Up = _Tp> 513 struct compare_three_way_result 514 : __detail::__cmp3way_res_impl<_Tp, _Up> 515 { }; 516 517 /// [cmp.result], result of three-way comparison 518 template<typename _Tp, typename _Up = _Tp> 519 using compare_three_way_result_t 520 = typename __detail::__cmp3way_res_impl<_Tp, _Up>::type; 521 522 namespace __detail 523 { 524 // BUILTIN-PTR-THREE-WAY(T, U) 525 // This determines whether t <=> u results in a call to a built-in 526 // operator<=> comparing pointers. It doesn't work for function pointers 527 // (PR 93628). 528 template<typename _Tp, typename _Up> 529 concept __3way_builtin_ptr_cmp 530 = requires(_Tp&& __t, _Up&& __u) 531 { static_cast<_Tp&&>(__t) <=> static_cast<_Up&&>(__u); } 532 && convertible_to<_Tp, const volatile void*> 533 && convertible_to<_Up, const volatile void*> 534 && ! requires(_Tp&& __t, _Up&& __u) 535 { operator<=>(static_cast<_Tp&&>(__t), static_cast<_Up&&>(__u)); } 536 && ! requires(_Tp&& __t, _Up&& __u) 537 { static_cast<_Tp&&>(__t).operator<=>(static_cast<_Up&&>(__u)); }; 538 } // namespace __detail 539 540 // _GLIBCXX_RESOLVE_LIB_DEFECTS 541 // 3530 BUILTIN-PTR-MEOW should not opt the type out of syntactic checks 542 543 // [cmp.object], typename compare_three_way 544 struct compare_three_way 545 { 546 template<typename _Tp, typename _Up> 547 requires three_way_comparable_with<_Tp, _Up> 548 constexpr auto 549 operator() [[nodiscard]] (_Tp&& __t, _Up&& __u) const 550 noexcept(noexcept(std::declval<_Tp>() <=> std::declval<_Up>())) 551 { 552 if constexpr (__detail::__3way_builtin_ptr_cmp<_Tp, _Up>) 553 { 554 auto __pt = static_cast<const volatile void*>(__t); 555 auto __pu = static_cast<const volatile void*>(__u); 556 if (std::__is_constant_evaluated()) 557 return __pt <=> __pu; 558 auto __it = reinterpret_cast<__UINTPTR_TYPE__>(__pt); 559 auto __iu = reinterpret_cast<__UINTPTR_TYPE__>(__pu); 560 return __it <=> __iu; 561 } 562 else 563 return static_cast<_Tp&&>(__t) <=> static_cast<_Up&&>(__u); 564 } 565 566 using is_transparent = void; 567 }; 568 569 /// @cond undocumented 570 // Namespace for helpers for the <compare> customization points. 571 namespace __compare 572 { 573 template<floating_point _Tp> 574 constexpr weak_ordering 575 __fp_weak_ordering(_Tp __e, _Tp __f) 576 { 577 // Returns an integer with the same sign as the argument, and magnitude 578 // indicating the classification: zero=1 subnorm=2 norm=3 inf=4 nan=5 579 auto __cat = [](_Tp __fp) -> int { 580 const int __sign = __builtin_signbit(__fp) ? -1 : 1; 581 if (__builtin_isnormal(__fp)) 582 return (__fp == 0 ? 1 : 3) * __sign; 583 if (__builtin_isnan(__fp)) 584 return 5 * __sign; 585 if (int __inf = __builtin_isinf_sign(__fp)) 586 return 4 * __inf; 587 return 2 * __sign; 588 }; 589 590 auto __po = __e <=> __f; 591 if (is_lt(__po)) 592 return weak_ordering::less; 593 else if (is_gt(__po)) 594 return weak_ordering::greater; 595 else if (__po == partial_ordering::equivalent) 596 return weak_ordering::equivalent; 597 else // unordered, at least one argument is NaN 598 { 599 // return -1 for negative nan, +1 for positive nan, 0 otherwise. 600 auto __isnan_sign = [](_Tp __fp) -> int { 601 return __builtin_isnan(__fp) 602 ? __builtin_signbit(__fp) ? -1 : 1 603 : 0; 604 }; 605 auto __ord = __isnan_sign(__e) <=> __isnan_sign(__f); 606 if (is_eq(__ord)) 607 return weak_ordering::equivalent; 608 else if (is_lt(__ord)) 609 return weak_ordering::less; 610 else 611 return weak_ordering::greater; 612 } 613 } 614 615 void strong_order() = delete; 616 617 template<typename _Tp, typename _Up> 618 concept __adl_strong = requires(_Tp&& __t, _Up&& __u) 619 { 620 strong_ordering(strong_order(static_cast<_Tp&&>(__t), 621 static_cast<_Up&&>(__u))); 622 }; 623 624 void weak_order() = delete; 625 626 template<typename _Tp, typename _Up> 627 concept __adl_weak = requires(_Tp&& __t, _Up&& __u) 628 { 629 weak_ordering(weak_order(static_cast<_Tp&&>(__t), 630 static_cast<_Up&&>(__u))); 631 }; 632 633 void partial_order() = delete; 634 635 template<typename _Tp, typename _Up> 636 concept __adl_partial = requires(_Tp&& __t, _Up&& __u) 637 { 638 partial_ordering(partial_order(static_cast<_Tp&&>(__t), 639 static_cast<_Up&&>(__u))); 640 }; 641 642 template<typename _Ord, typename _Tp, typename _Up> 643 concept __cmp3way = requires(_Tp&& __t, _Up&& __u, compare_three_way __c) 644 { 645 _Ord(__c(static_cast<_Tp&&>(__t), static_cast<_Up&&>(__u))); 646 }; 647 648 template<typename _Tp, typename _Up> 649 concept __strongly_ordered 650 = __adl_strong<_Tp, _Up> 651 || floating_point<remove_reference_t<_Tp>> 652 || __cmp3way<strong_ordering, _Tp, _Up>; 653 654 template<typename _Tp, typename _Up> 655 concept __decayed_same_as = same_as<decay_t<_Tp>, decay_t<_Up>>; 656 657 class _Strong_order 658 { 659 template<typename _Tp, typename _Up> 660 static constexpr bool 661 _S_noexcept() 662 { 663 if constexpr (floating_point<decay_t<_Tp>>) 664 return true; 665 else if constexpr (__adl_strong<_Tp, _Up>) 666 return noexcept(strong_ordering(strong_order(std::declval<_Tp>(), 667 std::declval<_Up>()))); 668 else if constexpr (__cmp3way<strong_ordering, _Tp, _Up>) 669 return noexcept(compare_three_way()(std::declval<_Tp>(), 670 std::declval<_Up>())); 671 } 672 673 friend class _Weak_order; 674 friend class _Strong_fallback; 675 676 // Names for the supported floating-point representations. 677 enum class _Fp_fmt 678 { 679 _Binary16, _Binary32, _Binary64, _Binary128, // IEEE 680 _X86_80bit, // x86 80-bit extended precision 681 _M68k_80bit, // m68k 80-bit extended precision 682 _Dbldbl, // IBM 128-bit double-double 683 _Bfloat16, // std::bfloat16_t 684 }; 685 686 #ifndef __cpp_using_enum 687 // XXX Remove these once 'using enum' support is ubiquitous. 688 static constexpr _Fp_fmt _Binary16 = _Fp_fmt::_Binary16; 689 static constexpr _Fp_fmt _Binary32 = _Fp_fmt::_Binary32; 690 static constexpr _Fp_fmt _Binary64 = _Fp_fmt::_Binary64; 691 static constexpr _Fp_fmt _Binary128 = _Fp_fmt::_Binary128; 692 static constexpr _Fp_fmt _X86_80bit = _Fp_fmt::_X86_80bit; 693 static constexpr _Fp_fmt _M68k_80bit = _Fp_fmt::_M68k_80bit; 694 static constexpr _Fp_fmt _Dbldbl = _Fp_fmt::_Dbldbl; 695 static constexpr _Fp_fmt _Bfloat16 = _Fp_fmt::_Bfloat16; 696 #endif 697 698 // Identify the format used by a floating-point type. 699 template<typename _Tp> 700 static consteval _Fp_fmt 701 _S_fp_fmt() noexcept 702 { 703 #ifdef __cpp_using_enum 704 using enum _Fp_fmt; 705 #endif 706 707 // Identify these formats first, then assume anything else is IEEE. 708 // N.B. ARM __fp16 alternative format can be handled as binary16. 709 710 #ifdef __LONG_DOUBLE_IBM128__ 711 if constexpr (__is_same(_Tp, long double)) 712 return _Dbldbl; 713 #elif defined __LONG_DOUBLE_IEEE128__ && defined __SIZEOF_IBM128__ 714 if constexpr (__is_same(_Tp, __ibm128)) 715 return _Dbldbl; 716 #endif 717 718 #if __LDBL_MANT_DIG__ == 64 719 if constexpr (__is_same(_Tp, long double)) 720 return __LDBL_MIN_EXP__ == -16381 ? _X86_80bit : _M68k_80bit; 721 #endif 722 #ifdef __SIZEOF_FLOAT80__ 723 if constexpr (__is_same(_Tp, __float80)) 724 return _X86_80bit; 725 #endif 726 #ifdef __STDCPP_BFLOAT16_T__ 727 if constexpr (__is_same(_Tp, decltype(0.0bf16))) 728 return _Bfloat16; 729 #endif 730 731 constexpr int __width = sizeof(_Tp) * __CHAR_BIT__; 732 733 if constexpr (__width == 16) // IEEE binary16 (or ARM fp16). 734 return _Binary16; 735 else if constexpr (__width == 32) // IEEE binary32 736 return _Binary32; 737 else if constexpr (__width == 64) // IEEE binary64 738 return _Binary64; 739 else if constexpr (__width == 128) // IEEE binary128 740 return _Binary128; 741 } 742 743 // So we don't need to include <stdint.h> and pollute the namespace. 744 using int64_t = __INT64_TYPE__; 745 using int32_t = __INT32_TYPE__; 746 using int16_t = __INT16_TYPE__; 747 using uint64_t = __UINT64_TYPE__; 748 using uint16_t = __UINT16_TYPE__; 749 750 // Used to unpack floating-point types that do not fit into an integer. 751 template<typename _Tp> 752 struct _Int 753 { 754 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 755 uint64_t _M_lo; 756 _Tp _M_hi; 757 #else 758 _Tp _M_hi; 759 uint64_t _M_lo; 760 #endif 761 762 constexpr explicit 763 _Int(_Tp __hi, uint64_t __lo) noexcept : _M_hi(__hi) 764 { _M_lo = __lo; } 765 766 constexpr explicit 767 _Int(uint64_t __lo) noexcept : _M_hi(0) 768 { _M_lo = __lo; } 769 770 constexpr bool operator==(const _Int&) const = default; 771 772 #if defined __hppa__ || (defined __mips__ && !defined __mips_nan2008) 773 consteval _Int 774 operator<<(int __n) const noexcept 775 { 776 // XXX this assumes n >= 64, which is true for the use below. 777 return _Int(static_cast<_Tp>(_M_lo << (__n - 64)), 0); 778 } 779 #endif 780 781 constexpr _Int& 782 operator^=(const _Int& __rhs) noexcept 783 { 784 _M_hi ^= __rhs._M_hi; 785 _M_lo ^= __rhs._M_lo; 786 return *this; 787 } 788 789 constexpr strong_ordering 790 operator<=>(const _Int& __rhs) const noexcept 791 { 792 strong_ordering __cmp = _M_hi <=> __rhs._M_hi; 793 if (__cmp != strong_ordering::equal) 794 return __cmp; 795 return _M_lo <=> __rhs._M_lo; 796 } 797 }; 798 799 template<typename _Tp> 800 static constexpr _Tp 801 _S_compl(_Tp __t) noexcept 802 { 803 constexpr int __width = sizeof(_Tp) * __CHAR_BIT__; 804 // Sign extend to get all ones or all zeros. 805 make_unsigned_t<_Tp> __sign = __t >> (__width - 1); 806 // If the sign bit was set, this flips all bits below it. 807 // This converts ones' complement to two's complement. 808 return __t ^ (__sign >> 1); 809 } 810 811 // As above but works on both parts of _Int<T>. 812 template<typename _Tp> 813 static constexpr _Int<_Tp> 814 _S_compl(_Int<_Tp> __t) noexcept 815 { 816 constexpr int __width = sizeof(_Tp) * __CHAR_BIT__; 817 make_unsigned_t<_Tp> __sign = __t._M_hi >> (__width - 1); 818 __t._M_hi ^= (__sign >> 1 ); 819 uint64_t __sign64 = (_Tp)__sign; 820 __t._M_lo ^= __sign64; 821 return __t; 822 } 823 824 // Bit-cast a floating-point value to an unsigned integer. 825 template<typename _Tp> 826 constexpr static auto 827 _S_fp_bits(_Tp __val) noexcept 828 { 829 if constexpr (sizeof(_Tp) == sizeof(int64_t)) 830 return __builtin_bit_cast(int64_t, __val); 831 else if constexpr (sizeof(_Tp) == sizeof(int32_t)) 832 return __builtin_bit_cast(int32_t, __val); 833 else if constexpr (sizeof(_Tp) == sizeof(int16_t)) 834 return __builtin_bit_cast(int16_t, __val); 835 else 836 { 837 #ifdef __cpp_using_enum 838 using enum _Fp_fmt; 839 #endif 840 constexpr auto __fmt = _S_fp_fmt<_Tp>(); 841 if constexpr (__fmt == _X86_80bit || __fmt == _M68k_80bit) 842 { 843 if constexpr (sizeof(_Tp) == 3 * sizeof(int32_t)) 844 { 845 auto __ival = __builtin_bit_cast(_Int<int32_t>, __val); 846 return _Int<int16_t>(__ival._M_hi, __ival._M_lo); 847 } 848 else 849 { 850 auto __ival = __builtin_bit_cast(_Int<int64_t>, __val); 851 return _Int<int16_t>(__ival._M_hi, __ival._M_lo); 852 } 853 } 854 else if constexpr (sizeof(_Tp) == 2 * sizeof(int64_t)) 855 { 856 #if __SIZEOF_INT128__ 857 return __builtin_bit_cast(__int128, __val); 858 #else 859 return __builtin_bit_cast(_Int<int64_t>, __val); 860 #endif 861 } 862 else 863 static_assert(sizeof(_Tp) == sizeof(int64_t), 864 "unsupported floating-point type"); 865 } 866 } 867 868 template<typename _Tp> 869 static constexpr strong_ordering 870 _S_fp_cmp(_Tp __x, _Tp __y) noexcept 871 { 872 #ifdef __vax__ 873 if (__builtin_isnan(__x) || __builtin_isnan(__y)) 874 { 875 int __ix = (bool) __builtin_isnan(__x); 876 int __iy = (bool) __builtin_isnan(__y); 877 __ix *= __builtin_signbit(__x) ? -1 : 1; 878 __iy *= __builtin_signbit(__y) ? -1 : 1; 879 return __ix <=> __iy; 880 } 881 else 882 return __builtin_bit_cast(strong_ordering, __x <=> __y); 883 #endif 884 885 auto __ix = _S_fp_bits(__x); 886 auto __iy = _S_fp_bits(__y); 887 888 if (__ix == __iy) 889 return strong_ordering::equal; // All bits are equal, we're done. 890 891 #ifdef __cpp_using_enum 892 using enum _Fp_fmt; 893 #endif 894 constexpr auto __fmt = _S_fp_fmt<_Tp>(); 895 896 if constexpr (__fmt == _Dbldbl) // double-double 897 { 898 // Unpack the double-double into two parts. 899 // We never inspect the low double as a double, cast to integer. 900 struct _Unpacked { double _M_hi; int64_t _M_lo; }; 901 auto __x2 = __builtin_bit_cast(_Unpacked, __x); 902 auto __y2 = __builtin_bit_cast(_Unpacked, __y); 903 904 // Compare the high doubles first and use result if unequal. 905 auto __cmp = _S_fp_cmp(__x2._M_hi, __y2._M_hi); 906 if (__cmp != strong_ordering::equal) 907 return __cmp; 908 909 // For NaN the low double is unused, so if the high doubles 910 // are the same NaN, we don't need to compare the low doubles. 911 if (__builtin_isnan(__x2._M_hi)) 912 return strong_ordering::equal; 913 // Similarly, if the low doubles are +zero or -zero (which is 914 // true for all infinities and some other values), we're done. 915 if (((__x2._M_lo | __y2._M_lo) & 0x7fffffffffffffffULL) == 0) 916 return strong_ordering::equal; 917 918 // Otherwise, compare the low parts. 919 return _S_compl(__x2._M_lo) <=> _S_compl(__y2._M_lo); 920 } 921 else 922 { 923 if constexpr (__fmt == _M68k_80bit) 924 { 925 // For m68k the MSB of the significand is ignored for the 926 // greatest exponent, so either 0 or 1 is valid there. 927 // Set it before comparing, so that we never have 0 there. 928 constexpr uint16_t __maxexp = 0x7fff; 929 if ((__ix._M_hi & __maxexp) == __maxexp) 930 __ix._M_lo |= 1ull << 63; 931 if ((__iy._M_hi & __maxexp) == __maxexp) 932 __iy._M_lo |= 1ull << 63; 933 } 934 else 935 { 936 #if defined __hppa__ || (defined __mips__ && !defined __mips_nan2008) 937 // IEEE 754-1985 allowed the meaning of the quiet/signaling 938 // bit to be reversed. Flip that to give desired ordering. 939 if (__builtin_isnan(__x) && __builtin_isnan(__y)) 940 { 941 using _Int = decltype(__ix); 942 943 constexpr int __nantype = __fmt == _Binary32 ? 22 944 : __fmt == _Binary64 ? 51 945 : __fmt == _Binary128 ? 111 946 : -1; 947 constexpr _Int __bit = _Int(1) << __nantype; 948 __ix ^= __bit; 949 __iy ^= __bit; 950 } 951 #endif 952 } 953 return _S_compl(__ix) <=> _S_compl(__iy); 954 } 955 } 956 957 public: 958 template<typename _Tp, __decayed_same_as<_Tp> _Up> 959 requires __strongly_ordered<_Tp, _Up> 960 constexpr strong_ordering 961 operator() [[nodiscard]] (_Tp&& __e, _Up&& __f) const 962 noexcept(_S_noexcept<_Tp, _Up>()) 963 { 964 if constexpr (floating_point<decay_t<_Tp>>) 965 return _S_fp_cmp(__e, __f); 966 else if constexpr (__adl_strong<_Tp, _Up>) 967 return strong_ordering(strong_order(static_cast<_Tp&&>(__e), 968 static_cast<_Up&&>(__f))); 969 else if constexpr (__cmp3way<strong_ordering, _Tp, _Up>) 970 return compare_three_way()(static_cast<_Tp&&>(__e), 971 static_cast<_Up&&>(__f)); 972 } 973 }; 974 975 template<typename _Tp, typename _Up> 976 concept __weakly_ordered 977 = floating_point<remove_reference_t<_Tp>> 978 || __adl_weak<_Tp, _Up> 979 || __cmp3way<weak_ordering, _Tp, _Up> 980 || __strongly_ordered<_Tp, _Up>; 981 982 class _Weak_order 983 { 984 template<typename _Tp, typename _Up> 985 static constexpr bool 986 _S_noexcept() 987 { 988 if constexpr (floating_point<decay_t<_Tp>>) 989 return true; 990 else if constexpr (__adl_weak<_Tp, _Up>) 991 return noexcept(weak_ordering(weak_order(std::declval<_Tp>(), 992 std::declval<_Up>()))); 993 else if constexpr (__cmp3way<weak_ordering, _Tp, _Up>) 994 return noexcept(compare_three_way()(std::declval<_Tp>(), 995 std::declval<_Up>())); 996 else if constexpr (__strongly_ordered<_Tp, _Up>) 997 return _Strong_order::_S_noexcept<_Tp, _Up>(); 998 } 999 1000 friend class _Partial_order; 1001 friend class _Weak_fallback; 1002 1003 public: 1004 template<typename _Tp, __decayed_same_as<_Tp> _Up> 1005 requires __weakly_ordered<_Tp, _Up> 1006 constexpr weak_ordering 1007 operator() [[nodiscard]] (_Tp&& __e, _Up&& __f) const 1008 noexcept(_S_noexcept<_Tp, _Up>()) 1009 { 1010 if constexpr (floating_point<decay_t<_Tp>>) 1011 return __compare::__fp_weak_ordering(__e, __f); 1012 else if constexpr (__adl_weak<_Tp, _Up>) 1013 return weak_ordering(weak_order(static_cast<_Tp&&>(__e), 1014 static_cast<_Up&&>(__f))); 1015 else if constexpr (__cmp3way<weak_ordering, _Tp, _Up>) 1016 return compare_three_way()(static_cast<_Tp&&>(__e), 1017 static_cast<_Up&&>(__f)); 1018 else if constexpr (__strongly_ordered<_Tp, _Up>) 1019 return _Strong_order{}(static_cast<_Tp&&>(__e), 1020 static_cast<_Up&&>(__f)); 1021 } 1022 }; 1023 1024 template<typename _Tp, typename _Up> 1025 concept __partially_ordered 1026 = __adl_partial<_Tp, _Up> 1027 || __cmp3way<partial_ordering, _Tp, _Up> 1028 || __weakly_ordered<_Tp, _Up>; 1029 1030 class _Partial_order 1031 { 1032 template<typename _Tp, typename _Up> 1033 static constexpr bool 1034 _S_noexcept() 1035 { 1036 if constexpr (__adl_partial<_Tp, _Up>) 1037 return noexcept(partial_ordering(partial_order(std::declval<_Tp>(), 1038 std::declval<_Up>()))); 1039 else if constexpr (__cmp3way<partial_ordering, _Tp, _Up>) 1040 return noexcept(compare_three_way()(std::declval<_Tp>(), 1041 std::declval<_Up>())); 1042 else if constexpr (__weakly_ordered<_Tp, _Up>) 1043 return _Weak_order::_S_noexcept<_Tp, _Up>(); 1044 } 1045 1046 friend class _Partial_fallback; 1047 1048 public: 1049 template<typename _Tp, __decayed_same_as<_Tp> _Up> 1050 requires __partially_ordered<_Tp, _Up> 1051 constexpr partial_ordering 1052 operator() [[nodiscard]] (_Tp&& __e, _Up&& __f) const 1053 noexcept(_S_noexcept<_Tp, _Up>()) 1054 { 1055 if constexpr (__adl_partial<_Tp, _Up>) 1056 return partial_ordering(partial_order(static_cast<_Tp&&>(__e), 1057 static_cast<_Up&&>(__f))); 1058 else if constexpr (__cmp3way<partial_ordering, _Tp, _Up>) 1059 return compare_three_way()(static_cast<_Tp&&>(__e), 1060 static_cast<_Up&&>(__f)); 1061 else if constexpr (__weakly_ordered<_Tp, _Up>) 1062 return _Weak_order{}(static_cast<_Tp&&>(__e), 1063 static_cast<_Up&&>(__f)); 1064 } 1065 }; 1066 1067 template<typename _Tp, typename _Up> 1068 concept __op_eq_lt = requires(_Tp&& __t, _Up&& __u) 1069 { 1070 { static_cast<_Tp&&>(__t) == static_cast<_Up&&>(__u) } 1071 -> convertible_to<bool>; 1072 { static_cast<_Tp&&>(__t) < static_cast<_Up&&>(__u) } 1073 -> convertible_to<bool>; 1074 }; 1075 1076 class _Strong_fallback 1077 { 1078 template<typename _Tp, typename _Up> 1079 static constexpr bool 1080 _S_noexcept() 1081 { 1082 if constexpr (__strongly_ordered<_Tp, _Up>) 1083 return _Strong_order::_S_noexcept<_Tp, _Up>(); 1084 else 1085 return noexcept(bool(std::declval<_Tp>() == std::declval<_Up>())) 1086 && noexcept(bool(std::declval<_Tp>() < std::declval<_Up>())); 1087 } 1088 1089 public: 1090 template<typename _Tp, __decayed_same_as<_Tp> _Up> 1091 requires __strongly_ordered<_Tp, _Up> || __op_eq_lt<_Tp, _Up> 1092 constexpr strong_ordering 1093 operator() [[nodiscard]] (_Tp&& __e, _Up&& __f) const 1094 noexcept(_S_noexcept<_Tp, _Up>()) 1095 { 1096 if constexpr (__strongly_ordered<_Tp, _Up>) 1097 return _Strong_order{}(static_cast<_Tp&&>(__e), 1098 static_cast<_Up&&>(__f)); 1099 else // __op_eq_lt<_Tp, _Up> 1100 return static_cast<_Tp&&>(__e) == static_cast<_Up&&>(__f) 1101 ? strong_ordering::equal 1102 : static_cast<_Tp&&>(__e) < static_cast<_Up&&>(__f) 1103 ? strong_ordering::less 1104 : strong_ordering::greater; 1105 } 1106 }; 1107 1108 class _Weak_fallback 1109 { 1110 template<typename _Tp, typename _Up> 1111 static constexpr bool 1112 _S_noexcept() 1113 { 1114 if constexpr (__weakly_ordered<_Tp, _Up>) 1115 return _Weak_order::_S_noexcept<_Tp, _Up>(); 1116 else 1117 return noexcept(bool(std::declval<_Tp>() == std::declval<_Up>())) 1118 && noexcept(bool(std::declval<_Tp>() < std::declval<_Up>())); 1119 } 1120 1121 public: 1122 template<typename _Tp, __decayed_same_as<_Tp> _Up> 1123 requires __weakly_ordered<_Tp, _Up> || __op_eq_lt<_Tp, _Up> 1124 constexpr weak_ordering 1125 operator() [[nodiscard]] (_Tp&& __e, _Up&& __f) const 1126 noexcept(_S_noexcept<_Tp, _Up>()) 1127 { 1128 if constexpr (__weakly_ordered<_Tp, _Up>) 1129 return _Weak_order{}(static_cast<_Tp&&>(__e), 1130 static_cast<_Up&&>(__f)); 1131 else // __op_eq_lt<_Tp, _Up> 1132 return static_cast<_Tp&&>(__e) == static_cast<_Up&&>(__f) 1133 ? weak_ordering::equivalent 1134 : static_cast<_Tp&&>(__e) < static_cast<_Up&&>(__f) 1135 ? weak_ordering::less 1136 : weak_ordering::greater; 1137 } 1138 }; 1139 1140 // _GLIBCXX_RESOLVE_LIB_DEFECTS 1141 // 3465. compare_partial_order_fallback requires F < E 1142 template<typename _Tp, typename _Up> 1143 concept __op_eq_lt_lt = __op_eq_lt<_Tp, _Up> 1144 && requires(_Tp&& __t, _Up&& __u) 1145 { 1146 { static_cast<_Up&&>(__u) < static_cast<_Tp&&>(__t) } 1147 -> convertible_to<bool>; 1148 }; 1149 1150 class _Partial_fallback 1151 { 1152 template<typename _Tp, typename _Up> 1153 static constexpr bool 1154 _S_noexcept() 1155 { 1156 if constexpr (__partially_ordered<_Tp, _Up>) 1157 return _Partial_order::_S_noexcept<_Tp, _Up>(); 1158 else 1159 return noexcept(bool(std::declval<_Tp>() == std::declval<_Up>())) 1160 && noexcept(bool(std::declval<_Tp>() < std::declval<_Up>())); 1161 } 1162 1163 public: 1164 template<typename _Tp, __decayed_same_as<_Tp> _Up> 1165 requires __partially_ordered<_Tp, _Up> || __op_eq_lt_lt<_Tp, _Up> 1166 constexpr partial_ordering 1167 operator() [[nodiscard]] (_Tp&& __e, _Up&& __f) const 1168 noexcept(_S_noexcept<_Tp, _Up>()) 1169 { 1170 if constexpr (__partially_ordered<_Tp, _Up>) 1171 return _Partial_order{}(static_cast<_Tp&&>(__e), 1172 static_cast<_Up&&>(__f)); 1173 else // __op_eq_lt_lt<_Tp, _Up> 1174 return static_cast<_Tp&&>(__e) == static_cast<_Up&&>(__f) 1175 ? partial_ordering::equivalent 1176 : static_cast<_Tp&&>(__e) < static_cast<_Up&&>(__f) 1177 ? partial_ordering::less 1178 : static_cast<_Up&&>(__f) < static_cast<_Tp&&>(__e) 1179 ? partial_ordering::greater 1180 : partial_ordering::unordered; 1181 } 1182 }; 1183 } // namespace @endcond 1184 1185 // [cmp.alg], comparison algorithms 1186 1187 inline namespace _Cpo 1188 { 1189 inline constexpr __compare::_Strong_order strong_order{}; 1190 1191 inline constexpr __compare::_Weak_order weak_order{}; 1192 1193 inline constexpr __compare::_Partial_order partial_order{}; 1194 1195 inline constexpr __compare::_Strong_fallback 1196 compare_strong_order_fallback{}; 1197 1198 inline constexpr __compare::_Weak_fallback 1199 compare_weak_order_fallback{}; 1200 1201 inline constexpr __compare::_Partial_fallback 1202 compare_partial_order_fallback{}; 1203 } 1204 1205 /// @cond undocumented 1206 namespace __detail 1207 { 1208 // [expos.only.func] synth-three-way 1209 inline constexpr struct _Synth3way 1210 { 1211 template<typename _Tp, typename _Up> 1212 static constexpr bool 1213 _S_noexcept(const _Tp* __t = nullptr, const _Up* __u = nullptr) 1214 { 1215 if constexpr (three_way_comparable_with<_Tp, _Up>) 1216 return noexcept(*__t <=> *__u); 1217 else 1218 return noexcept(*__t < *__u) && noexcept(*__u < *__t); 1219 } 1220 1221 template<typename _Tp, typename _Up> 1222 [[nodiscard]] 1223 constexpr auto 1224 operator()(const _Tp& __t, const _Up& __u) const 1225 noexcept(_S_noexcept<_Tp, _Up>()) 1226 requires requires 1227 { 1228 { __t < __u } -> __boolean_testable; 1229 { __u < __t } -> __boolean_testable; 1230 } 1231 { 1232 if constexpr (three_way_comparable_with<_Tp, _Up>) 1233 return __t <=> __u; 1234 else 1235 { 1236 if (__t < __u) 1237 return weak_ordering::less; 1238 else if (__u < __t) 1239 return weak_ordering::greater; 1240 else 1241 return weak_ordering::equivalent; 1242 } 1243 } 1244 } __synth3way = {}; 1245 1246 // [expos.only.func] synth-three-way-result 1247 template<typename _Tp, typename _Up = _Tp> 1248 using __synth3way_t 1249 = decltype(__detail::__synth3way(std::declval<_Tp&>(), 1250 std::declval<_Up&>())); 1251 } // namespace __detail 1252 /// @endcond 1253 #endif // __cpp_lib_three_way_comparison >= 201907L 1254 } // namespace std 1255 1256 #endif // C++20 1257 1258 #endif // _COMPARE 1259