From patchwork Fri Nov 17 15:29:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 166259 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9910:0:b0:403:3b70:6f57 with SMTP id i16csp656330vqn; Fri, 17 Nov 2023 08:35:54 -0800 (PST) X-Google-Smtp-Source: AGHT+IEZacAQvx5Kvz+6QuwdYZazLNX3ur9iX9NlwOvKtFMho7QJa52QbCEFhMkEBtvT+3I/8GTX X-Received: by 2002:a05:6870:bf13:b0:1d6:3d31:fb4b with SMTP id qh19-20020a056870bf1300b001d63d31fb4bmr26142098oab.24.1700238953905; Fri, 17 Nov 2023 08:35:53 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1700238953; cv=pass; d=google.com; s=arc-20160816; b=M/q84aZ62nfmtG4phAg48rHJ/FZv2aew2LbblPwyYdkso7EZpzP01fIUcVIPE6sCRW sW3jSINTki9gc92DUjhIeLDDHgvb4jSqSVgubQ6UG/4y+Qd0WjAvVwAwJDzmbvBhWwBb F0t419y86mYpzZHRlpOI2pptwELmgpSn6jYyyWTOfQ3+AAtxN3PuavCEO+pvct/RSw7p tYXw1IGExAKUwbwVSz5rrBfUaywOVarBT1ZA1pBtPokyeX/1XkpWQ/5AetOD+wLQZGkx 7E405GP7jQlVQ3KFOxXipWanlxTNFPc76LVpDeV5JAHSPHC+rNna0IJzqhRJ72Jrw3Y5 ypAg== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:message-id:date:subject:to:from:dkim-signature :arc-filter:dmarc-filter:delivered-to; bh=VUMvSXTy7KCWFSR6eakpIAc0A+YfOfyiZD1o9WJfuio=; fh=sJ+2/4g29YdyXkoRrFZSpsL2zxijepB7X/1rB0LDDh8=; b=cnmkCm6ChZqi7jodYQGy0T6ayusshiHH64/W1vEF66R58zvysK2xqiOt7v8hp8zyO6 NYOrhIlh2iwU6Jn7JyezTOPDgbifZDbTX+vCYDvss+mzozDT+R1dxM4hGe1nAQrwlrDf Pk16lUDayOIFry9exGgQjWhSOC+JJU8jFEjX7XfR1hqaRQ1thvVB3OFz0eocWCJHInr7 /a/66Zxdl48vrxysaaN9L/wj3RczM/CoMaOuvYx+uy5+dMnukAnRAGF0sgq013mBdl7l w8Rroy9pvzDyxPqB5ZNVTYmJOkb3DuZxJde4PxXfo+qj8uQonTPM8u+wfh01WF8chdl5 Vx3Q== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=F9fRztQA; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (server2.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id t20-20020a05622a01d400b0041092ca3dd6si1883982qtw.306.2023.11.17.08.35.53 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Nov 2023 08:35:53 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=F9fRztQA; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 36894387545C for ; Fri, 17 Nov 2023 15:32:17 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id D3EFC3858D35 for ; Fri, 17 Nov 2023 15:31:41 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D3EFC3858D35 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org D3EFC3858D35 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700235112; cv=none; b=njvO7zRvlk8NWhlT7L0+8n0Ni+PBKOobjfAw4gOzDj6MfwblDkOXonvPYjyCMFT3kq2nrLGXOqPBQCA4xL1l7I0MECNzALy8o5DSNqZeU2c+rFqdIvQVlmuYm1P4F4hZ7efprUB0LuGZv+fh6IKs2EmWfQ9FKQ86r9p9LGOVL9I= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700235112; c=relaxed/simple; bh=blk6YGSv+k+mR/DMzolDgFGVfotvmPyl6T4acxoXcQI=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=bs4VcSfOuqg0lVNSKvgmY0SX+D5hmux0okZxAMoFkNBrt/ie2DtP4VP7vBcnq01ue9oxcsTXz2Bb19LXHgdTFqfSlXnbAunK9MjeZbLuh46gUiHtzHpVhwMMl8F7jpwsm12sX8TYJ7pWJLihMLNwLnXKviQP9rH6V5202do0rys= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700235101; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=VUMvSXTy7KCWFSR6eakpIAc0A+YfOfyiZD1o9WJfuio=; b=F9fRztQA9IIS8sdqGTlptX1b3+5QTcIRq1rpc6fbf3GDslyOfCTgaTlD9nG9o/F5rXoaFy ZcohxgztT2rZWFgTHHgAdNQFpx64YEkcz7qWdJo0iGL/7iaf2A+6DRncFfkD7SKtXf6vwu 4zAbfZ/wlYs+v6/+pc4pZ4WZyMuYco0= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-231-9e-cDngpO5usiApvTl9lsA-1; Fri, 17 Nov 2023 10:31:39 -0500 X-MC-Unique: 9e-cDngpO5usiApvTl9lsA-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id EA8E138116E8; Fri, 17 Nov 2023 15:31:38 +0000 (UTC) Received: from localhost (unknown [10.42.28.9]) by smtp.corp.redhat.com (Postfix) with ESMTP id 99408503A; Fri, 17 Nov 2023 15:31:38 +0000 (UTC) From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Define C++26 saturation arithmetic functions (P0543R3) Date: Fri, 17 Nov 2023 15:29:17 +0000 Message-ID: <20231117153138.1510158-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.5 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.3 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE, URIBL_BLACK autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1782829760915197875 X-GMAIL-MSGID: 1782829760915197875 Tested x86_64-linux. Pushed to trunk. GCC generates better code for add_sat if we use: unsigned z = x + y; z |= -(z < x); return z; If the compiler can't be improved we should consider using that instead of __builtin_add_overflow. -- >8 -- This was approved for C++26 last week at the WG21 meeting in Kona. libstdc++-v3/ChangeLog: * include/Makefile.am: Add new header. * include/Makefile.in: Regenerate. * include/bits/version.def (saturation_arithmetic): Define. * include/bits/version.h: Regenerate. * include/std/numeric: Include new header. * include/bits/sat_arith.h: New file. * testsuite/26_numerics/saturation/add.cc: New test. * testsuite/26_numerics/saturation/cast.cc: New test. * testsuite/26_numerics/saturation/div.cc: New test. * testsuite/26_numerics/saturation/mul.cc: New test. * testsuite/26_numerics/saturation/sub.cc: New test. * testsuite/26_numerics/saturation/version.cc: New test. --- libstdc++-v3/include/Makefile.am | 1 + libstdc++-v3/include/Makefile.in | 1 + libstdc++-v3/include/bits/sat_arith.h | 148 ++++++++++++++++++ libstdc++-v3/include/bits/version.def | 8 + libstdc++-v3/include/bits/version.h | 11 ++ libstdc++-v3/include/std/numeric | 5 + .../testsuite/26_numerics/saturation/add.cc | 73 +++++++++ .../testsuite/26_numerics/saturation/cast.cc | 24 +++ .../testsuite/26_numerics/saturation/div.cc | 45 ++++++ .../testsuite/26_numerics/saturation/mul.cc | 34 ++++ .../testsuite/26_numerics/saturation/sub.cc | 86 ++++++++++ .../26_numerics/saturation/version.cc | 19 +++ 12 files changed, 455 insertions(+) create mode 100644 libstdc++-v3/include/bits/sat_arith.h create mode 100644 libstdc++-v3/testsuite/26_numerics/saturation/add.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/saturation/cast.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/saturation/div.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/saturation/mul.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/saturation/sub.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/saturation/version.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index dab9f720cbb..17d9d9cec31 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -142,6 +142,7 @@ bits_freestanding = \ ${bits_srcdir}/ranges_uninitialized.h \ ${bits_srcdir}/ranges_util.h \ ${bits_srcdir}/refwrap.h \ + ${bits_srcdir}/sat_arith.h \ ${bits_srcdir}/stl_algo.h \ ${bits_srcdir}/stl_algobase.h \ ${bits_srcdir}/stl_construct.h \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index 4f7ab2dfbab..f038af709cc 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -497,6 +497,7 @@ bits_freestanding = \ ${bits_srcdir}/ranges_uninitialized.h \ ${bits_srcdir}/ranges_util.h \ ${bits_srcdir}/refwrap.h \ + ${bits_srcdir}/sat_arith.h \ ${bits_srcdir}/stl_algo.h \ ${bits_srcdir}/stl_algobase.h \ ${bits_srcdir}/stl_construct.h \ diff --git a/libstdc++-v3/include/bits/sat_arith.h b/libstdc++-v3/include/bits/sat_arith.h new file mode 100644 index 00000000000..71793467984 --- /dev/null +++ b/libstdc++-v3/include/bits/sat_arith.h @@ -0,0 +1,148 @@ +// Saturation arithmetic -*- C++ -*- + +// Copyright The GNU Toolchain Authors. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file include/bits/sat_arith.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{numeric} + */ + +#ifndef _GLIBCXX_SAT_ARITH_H +#define _GLIBCXX_SAT_ARITH_H 1 + +#pragma GCC system_header + +#include + +#ifdef __glibcxx_saturation_arithmetic // C++ >= 26 + +#include +#include + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + /// Add two integers, with saturation in case of overflow. + template requires __is_standard_integer<_Tp>::value + constexpr _Tp + add_sat(_Tp __x, _Tp __y) noexcept + { + _Tp __z; + if (!__builtin_add_overflow(__x, __y, &__z)) + return __z; + if constexpr (is_unsigned_v<_Tp>) + return __gnu_cxx::__int_traits<_Tp>::__max; + else if (__x < 0) + return __gnu_cxx::__int_traits<_Tp>::__min; + else + return __gnu_cxx::__int_traits<_Tp>::__max; + } + + /// Subtract one integer from another, with saturation in case of overflow. + template requires __is_standard_integer<_Tp>::value + constexpr _Tp + sub_sat(_Tp __x, _Tp __y) noexcept + { + _Tp __z; + if (!__builtin_sub_overflow(__x, __y, &__z)) + return __z; + if constexpr (is_unsigned_v<_Tp>) + return __gnu_cxx::__int_traits<_Tp>::__min; + else if (__x < 0) + return __gnu_cxx::__int_traits<_Tp>::__min; + else + return __gnu_cxx::__int_traits<_Tp>::__max; + } + + /// Multiply two integers, with saturation in case of overflow. + template requires __is_standard_integer<_Tp>::value + constexpr _Tp + mul_sat(_Tp __x, _Tp __y) noexcept + { + _Tp __z; + if (!__builtin_mul_overflow(__x, __y, &__z)) + return __z; + if constexpr (is_unsigned_v<_Tp>) + return __gnu_cxx::__int_traits<_Tp>::__max; + else if (__x < 0 != __y < 0) + return __gnu_cxx::__int_traits<_Tp>::__min; + else + return __gnu_cxx::__int_traits<_Tp>::__max; + } + + /// Divide one integer by another, with saturation in case of overflow. + template requires __is_standard_integer<_Tp>::value + constexpr _Tp + div_sat(_Tp __x, _Tp __y) noexcept + { + __glibcxx_assert(__y != 0); + if constexpr (is_signed_v<_Tp>) + if (__x == __gnu_cxx::__int_traits<_Tp>::__min && __y == _Tp(-1)) + return __gnu_cxx::__int_traits<_Tp>::__max; + return __x / __y; + } + + /// Divide one integer by another, with saturation in case of overflow. + template + requires __is_standard_integer<_Res>::value + && __is_standard_integer<_Tp>::value + constexpr _Res + saturate_cast(_Tp __x) noexcept + { + constexpr int __digits_R = __gnu_cxx::__int_traits<_Res>::__digits; + constexpr int __digits_T = __gnu_cxx::__int_traits<_Tp>::__digits; + constexpr _Res __max_Res = __gnu_cxx::__int_traits<_Res>::__max; + + if constexpr (is_signed_v<_Res> == is_signed_v<_Tp>) + { + if constexpr (__digits_R < __digits_T) + { + constexpr _Res __min_Res = __gnu_cxx::__int_traits<_Res>::__min; + + if (__x < static_cast<_Tp>(__min_Res)) + return __min_Res; + else if (__x > static_cast<_Tp>(__max_Res)) + return __max_Res; + } + } + else if constexpr (is_signed_v<_Tp>) // Res is unsigned + { + if (__x < 0) + return 0; + else if (make_unsigned_t<_Tp>(__x) > __max_Res) + return __gnu_cxx::__int_traits<_Res>::__max; + } + else // Tp is unsigned, Res is signed + { + if (__x > make_unsigned_t<_Res>(__max_Res)) + return __max_Res; + } + return static_cast<_Res>(__x); + } + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace + +#endif // __glibcxx_saturation_arithmetic +#endif /* _GLIBCXX_SAT_ARITH_H */ diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 60400b3b8d2..447fdeb9519 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1631,6 +1631,14 @@ ftms = { }; }; +ftms = { + name = saturation_arithmetic; + values = { + v = 202311; + cxxmin = 26; + }; +}; + ftms = { name = to_string; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 2e2af3e0c41..97c6d8508f4 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2000,6 +2000,17 @@ #undef __glibcxx_want_ratio // from version.def line 1635 +#if !defined(__cpp_lib_saturation_arithmetic) +# if (__cplusplus > 202302L) +# define __glibcxx_saturation_arithmetic 202311L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_saturation_arithmetic) +# define __cpp_lib_saturation_arithmetic 202311L +# endif +# endif +#endif /* !defined(__cpp_lib_saturation_arithmetic) && defined(__glibcxx_want_saturation_arithmetic) */ +#undef __glibcxx_want_saturation_arithmetic + +// from version.def line 1643 #if !defined(__cpp_lib_to_string) # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && (__glibcxx_to_chars) # define __glibcxx_to_string 202306L diff --git a/libstdc++-v3/include/std/numeric b/libstdc++-v3/include/std/numeric index 559e6242e92..fa8ef51c447 100644 --- a/libstdc++-v3/include/std/numeric +++ b/libstdc++-v3/include/std/numeric @@ -86,8 +86,13 @@ #define __glibcxx_want_lcm #define __glibcxx_want_parallel_algorithm #define __glibcxx_want_ranges_iota +#define __glibcxx_want_saturation_arithmetic #include +#ifdef __glibcxx_saturation_arithmetic // C++ >= 26 +# include +#endif + /** * @defgroup numerics Numerics * diff --git a/libstdc++-v3/testsuite/26_numerics/saturation/add.cc b/libstdc++-v3/testsuite/26_numerics/saturation/add.cc new file mode 100644 index 00000000000..3eee6abca9a --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/saturation/add.cc @@ -0,0 +1,73 @@ +// { dg-do compile { target c++26 } } + +// C++26 Saturation arithmetic [numerics.sat] + +#include +#include + +template +concept can_add_sat + = requires(T t, U u) { { std::add_sat(t, u) } -> std::same_as; }; + +static_assert( can_add_sat ); +static_assert( not can_add_sat ); +static_assert( not can_add_sat ); +static_assert( noexcept(std::add_sat(0, 0)) ); + +using std::add_sat; + +// Signed type +static_assert(add_sat(0, 0) == 0); +static_assert(add_sat(1, 1) == 2); +static_assert(add_sat(-1, -1) == -2); +static_assert(add_sat(-1, 1) == 0); +constexpr auto max = std::numeric_limits::max(); +constexpr auto min = std::numeric_limits::min(); +static_assert(add_sat(max, 1) == max); +static_assert(add_sat(1, max) == max); +static_assert(add_sat(max, max) == max); +static_assert(add_sat(min, -1) == min); +static_assert(add_sat(-1, min) == min); +static_assert(add_sat(min, min) == min); +static_assert(add_sat(max, min) == -1); +static_assert(add_sat(min, max) == -1); + +// Wider signed type than the args +static_assert(add_sat(max, max) == (long long)max * 2); +static_assert(add_sat(min, min) == (long long)min * 2); + +// Signed type that undergoes integer promotion +constexpr auto shrt_max = std::numeric_limits::max(); +constexpr auto shrt_min = std::numeric_limits::min(); +static_assert(add_sat(0, 0) == 0); +static_assert(add_sat(1, 1) == 2); +static_assert(add_sat(shrt_max, shrt_max) == shrt_max); +static_assert(add_sat(shrt_max, 1) == shrt_max); +static_assert(add_sat(1, shrt_max) == shrt_max); +static_assert(add_sat(shrt_min, (short)-1) == shrt_min); +static_assert(add_sat((short)-1, shrt_min) == shrt_min); +static_assert(add_sat(shrt_min, (short)1) == -shrt_max); +static_assert(add_sat((short)1, shrt_min) == -shrt_max); + +// Unsigned type +static_assert(add_sat(0u, 0u) == 0u); +static_assert(add_sat(1u, 1u) == 2u); +constexpr auto umax = std::numeric_limits::max(); +static_assert(add_sat(umax, 1u) == umax); +static_assert(add_sat(1u, umax) == umax); +static_assert(add_sat(umax, umax) == umax); +static_assert(add_sat(0u, umax) == umax); +static_assert(add_sat(umax, 0u) == umax); +static_assert(add_sat(0u, 1u) == 1u); +static_assert(add_sat(1u, 0u) == 1u); + +// Wider unsigned type than the args +static_assert(add_sat(umax, umax) == (long long)umax * 2); + +// Unsigned type that undergoes integer promotion +constexpr auto ushrt_max = std::numeric_limits::max(); +static_assert(add_sat(0, 0) == 0); +static_assert(add_sat(1, 1) == 2); +static_assert(add_sat(ushrt_max, ushrt_max) == ushrt_max); +static_assert(add_sat(ushrt_max, 1) == ushrt_max); +static_assert(add_sat(1, ushrt_max) == ushrt_max); diff --git a/libstdc++-v3/testsuite/26_numerics/saturation/cast.cc b/libstdc++-v3/testsuite/26_numerics/saturation/cast.cc new file mode 100644 index 00000000000..672e9c46be1 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/saturation/cast.cc @@ -0,0 +1,24 @@ +// { dg-do compile { target c++26 } } + +// C++26 Saturation arithmetic [numerics.sat] + +#include +#include + +#if CHAR_BIT == 8 +static_assert(std::saturate_cast(999) == 255); +static_assert(std::saturate_cast(999) == 127); +#endif +static_assert(std::saturate_cast(999) == 999); +static_assert(std::saturate_cast(999) == 999); +static_assert(std::saturate_cast(INT_MAX) == SHRT_MAX); +static_assert(std::saturate_cast(UINT_MAX) == SHRT_MAX); +static_assert(std::saturate_cast(UINT_MAX) == SHRT_MAX); +static_assert(std::saturate_cast(UINT_MAX) == USHRT_MAX); +static_assert(std::saturate_cast(UINT_MAX) == INT_MAX); +static_assert(std::saturate_cast(INT_MAX) == INT_MAX); +static_assert(std::saturate_cast(-1) == 0); +static_assert(std::saturate_cast(INT_MIN) == 0); +static_assert(std::saturate_cast(UINT_MAX) == UINT_MAX); +static_assert(std::saturate_cast(LLONG_MAX) == UINT_MAX); +static_assert(std::saturate_cast(ULLONG_MAX) == UINT_MAX); diff --git a/libstdc++-v3/testsuite/26_numerics/saturation/div.cc b/libstdc++-v3/testsuite/26_numerics/saturation/div.cc new file mode 100644 index 00000000000..a3f43573651 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/saturation/div.cc @@ -0,0 +1,45 @@ +// { dg-do compile { target c++26 } } + +// C++26 Saturation arithmetic [numerics.sat] + +#include +#include + +template +concept can_div_sat + = requires(T t, U u) { { std::div_sat(t, u) } -> std::same_as; }; + +static_assert( can_div_sat ); +static_assert( not can_div_sat ); +static_assert( not can_div_sat ); +static_assert( noexcept(std::div_sat(0, 1)) ); + +using std::div_sat; + +static_assert(std::div_sat(0, 1) == 0); +static_assert(std::div_sat(0, -1) == 0); +static_assert(std::div_sat(1, -1) == -1); +static_assert(std::div_sat(10, -2) == -5); +static_assert(std::div_sat(-10, -2) == 5); +static_assert(std::div_sat(INT_MAX, 1) == INT_MAX); +static_assert(std::div_sat(INT_MIN, 1) == INT_MIN); +static_assert(std::div_sat(INT_MIN + 1, -1) == INT_MAX); +static_assert(std::div_sat(0u, 1u) == 0u); +static_assert(std::div_sat(UINT_MAX, 1u) == UINT_MAX); +static_assert(std::div_sat(INT_MIN, -1) == INT_MAX); +static_assert(std::div_sat((short)SHRT_MIN, (short)-1) == SHRT_MAX); +static_assert(std::div_sat(LONG_MIN, -1L) == LONG_MAX); +static_assert(std::div_sat(LLONG_MIN, -1LL) == LLONG_MAX); + +template +std::integral_constant +div_sat_by_zero(); + +template +concept can_div_sat_by_zero = requires { div_sat_by_zero(); }; + +static_assert( not can_div_sat_by_zero<0> ); +static_assert( not can_div_sat_by_zero<1> ); +static_assert( not can_div_sat_by_zero<1u> ); +static_assert( not can_div_sat_by_zero<-1L> ); +static_assert( not can_div_sat_by_zero ); diff --git a/libstdc++-v3/testsuite/26_numerics/saturation/mul.cc b/libstdc++-v3/testsuite/26_numerics/saturation/mul.cc new file mode 100644 index 00000000000..1b5538c9af3 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/saturation/mul.cc @@ -0,0 +1,34 @@ +// { dg-do compile { target c++26 } } + +// C++26 Saturation arithmetic [numerics.sat] + +#include +#include + +template +concept can_mul_sat + = requires(T t, U u) { { std::mul_sat(t, u) } -> std::same_as; }; + +static_assert( can_mul_sat ); +static_assert( not can_mul_sat ); +static_assert( not can_mul_sat ); +static_assert( noexcept(std::mul_sat(0, 0)) ); + +using std::mul_sat; + +static_assert(mul_sat(1, 1) == 1); +static_assert(mul_sat(10, 11) == 110); +static_assert(mul_sat(INT_MAX / 2, 3) == INT_MAX); +static_assert(mul_sat(INT_MAX / 2, -3) == INT_MIN); +static_assert(mul_sat(INT_MAX / -2, 3) == INT_MIN); +static_assert(mul_sat(INT_MIN / 2, -3) == INT_MAX); +static_assert(mul_sat(INT_MIN, -1) == INT_MAX); +static_assert(mul_sat(INT_MAX, -1) == INT_MIN + 1); +static_assert(mul_sat(INT_MAX, INT_MAX) == INT_MAX); +static_assert(mul_sat(INT_MAX, -INT_MAX) == INT_MIN); +static_assert(mul_sat(UINT_MAX, UINT_MAX) == UINT_MAX); +static_assert(mul_sat(UINT_MAX, 0u) == 0); +static_assert(mul_sat(0u, UINT_MAX) == 0); +static_assert(mul_sat((short)SHRT_MAX, (short)2) == SHRT_MAX); +static_assert(mul_sat((short)SHRT_MAX, (short)SHRT_MIN) == SHRT_MIN); +static_assert(mul_sat(SHRT_MAX, 2) == 2L * SHRT_MAX); diff --git a/libstdc++-v3/testsuite/26_numerics/saturation/sub.cc b/libstdc++-v3/testsuite/26_numerics/saturation/sub.cc new file mode 100644 index 00000000000..c56154a49ff --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/saturation/sub.cc @@ -0,0 +1,86 @@ +// { dg-do compile { target c++26 } } + +// C++26 Saturation arithmetic [numerics.sat] + +#include +#include + +template +concept can_sub_sat + = requires(T t, U u) { { std::sub_sat(t, u) } -> std::same_as; }; + +static_assert( can_sub_sat ); +static_assert( not can_sub_sat ); +static_assert( not can_sub_sat ); +static_assert( noexcept(std::sub_sat(0, 0)) ); + +using std::sub_sat; + +// Signed type +static_assert(sub_sat(0, 0) == 0); +static_assert(sub_sat(1, 1) == 0); +static_assert(sub_sat(-1, -1) == 0); +static_assert(sub_sat(-1, 1) == -2); +constexpr auto max = std::numeric_limits::max(); +constexpr auto min = std::numeric_limits::min(); +static_assert(sub_sat(max, 1) == max - 1); +static_assert(sub_sat(1, max) == 1 - max); +static_assert(sub_sat(max, max) == 0); +static_assert(sub_sat(min, 1) == min); +static_assert(sub_sat(min, 123) == min); +static_assert(sub_sat(0, max) == min + 1); +static_assert(sub_sat(-1, max) == min); +static_assert(sub_sat(-2, max) == min); +static_assert(sub_sat(-2, min) == max - 1); +static_assert(sub_sat(-1, min) == max); +static_assert(sub_sat(0, min) == max); +static_assert(sub_sat(1, min) == max); +static_assert(sub_sat(min, -1) == min + 1); +static_assert(sub_sat(min, min) == 0); +static_assert(sub_sat(max, min) == max); +static_assert(sub_sat(min, max) == min); + +// Wider signed type than the args +static_assert(sub_sat(max, min) == (long long)max * 2 + 1); +static_assert(sub_sat(min, max) == (long long)min * 2 + 1); + +// Signed type that undergoes integer promotion +constexpr auto shrt_max = std::numeric_limits::max(); +constexpr auto shrt_min = std::numeric_limits::min(); +static_assert(sub_sat(0, 0) == 0); +static_assert(sub_sat(1, 1) == 0); +static_assert(sub_sat(3, 1) == 2); +static_assert(sub_sat(shrt_max, shrt_max) == 0); +static_assert(sub_sat(shrt_max, 1) == shrt_max - 1); +static_assert(sub_sat(1, shrt_max) == shrt_min + 2); +static_assert(sub_sat(shrt_max, shrt_min) == shrt_max); +static_assert(sub_sat(0, shrt_min) == shrt_max); +static_assert(sub_sat(shrt_min, (short)1) == shrt_min); +static_assert(sub_sat(shrt_min, (short)-1) == shrt_min + 1); +static_assert(sub_sat((short)-1, shrt_min) == shrt_max); +static_assert(sub_sat((short)1, shrt_min) == shrt_max); + +// Unsigned type +static_assert(sub_sat(0u, 0u) == 0u); +static_assert(sub_sat(1u, 1u) == 0u); +static_assert(sub_sat(-1u, -1u) == 0u); +static_assert(sub_sat(-1u, 1u) == -2u); +constexpr auto umax = std::numeric_limits::max(); +static_assert(sub_sat(0u, 1u) == 0u); +static_assert(sub_sat(umax, umax) == 0u); +static_assert(sub_sat(umax, 0u) == umax); +static_assert(sub_sat(0u, umax) == 0u); +static_assert(sub_sat(umax, 1u) == umax - 1u); +static_assert(sub_sat(0u, 0u) == 0u); + +// Wider unsigned type than the args +static_assert(sub_sat(0u, umax) == 0u); + +// Unsigned type that undergoes integer promotion +constexpr auto ushrt_max = std::numeric_limits::max(); +static_assert(sub_sat(0, 0) == 0); +static_assert(sub_sat(1, 1) == 0); +static_assert(sub_sat(3, 1) == 2); +static_assert(sub_sat(ushrt_max, ushrt_max) == 0); +static_assert(sub_sat(0, 1) == 0); +static_assert(sub_sat(1, ushrt_max) == 0); diff --git a/libstdc++-v3/testsuite/26_numerics/saturation/version.cc b/libstdc++-v3/testsuite/26_numerics/saturation/version.cc new file mode 100644 index 00000000000..ca94ef1212a --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/saturation/version.cc @@ -0,0 +1,19 @@ +// { dg-do preprocess { target c++26 } } +// { dg-add-options no_pch } + +#include + +#ifndef __cpp_lib_saturation_arithmetic +# error "Feature test macro for saturation arithmetic is missing in " +#elif __cpp_lib_saturation_arithmetic < 202311L +# error "Feature test macro for saturation arithmetic has wrong value in " +#endif + +#undef __cpp_lib_saturation_arithmetic +#include + +#ifndef __cpp_lib_saturation_arithmetic +# error "Feature test macro for saturation arithmetic is missing in " +#elif __cpp_lib_saturation_arithmetic < 202311L +# error "Feature test macro for saturation arithmetic has wrong value in " +#endif