1. Revision History
R0 => R1
-
Added implementation experience.
-
Improved wording.
2. Motivation
[P1928R4] introduced data parallel types to C++. It mostly provided operators
which worked on or with types, but it also included overloads of
useful functions from other parts of C++ (e.g., sin, cos, abs).
Furthermore, in [P0543R3] a proposal is made to provide saturating operation support for
some basic arithmetic operations and casts. In particular, , , and are provided. These perform saturating arithmetic
operations which are effectively performed in infinite precision, and will
return the smallest or largest value when it is too large to be represented in
that type. In addition, is also provided to convert to a new
type, and to saturate to the range of that type if required.
These saturating functions should be provided in as element-wise operations.
3. Implementation Experience
The most common types of saturing operations are addition, subtraction, and
casting. All three of these functions have been implemented in Intel’s reference
implementation and used in our software products. Where hardware support is
available for a data type these functions compile into native instructions
(e.g., 16-bit integer saturations compile into , , and respectively). For data types which have no saturating support in the
hardware for those three functions (e.g., large integers) the compiler can
generate efficient code to perform the operation (in the case of LLVM the function is used to hand this task to the compiler, rather
than having the library itself generate the required code sequence). Examples of
native versus non-native instruction sequences are given here:
| Source | Output from clang 20 |
|
|
|
|
|
|
The other saturating operations haven’t been implemented in the reference software as they are rarely needed. However, they can be trivially implemented in terms of the existing draft C++26 support for scalar saturating operations, or an optimized equivalent can be synthesized.
4. Wording
4.1. Modify [version.syn]
In [version.syn] bump the version.
4.2. Modify [simd.syn]
In the header synopsis - [simd.syn] - add at the end after the "Complex Math" functions.
template < simd - floating - point V > rebind_t < complex < typename V :: value_type > , V > polar ( const V & x , const V & y = {}); template < simd - complex V > constexpr V pow ( const V & x , const V & y ); // [simd.saturating.math], saturating math functions template < simd - type V > constexpr V add_sat ( const V & x , const V & y ) noexcept ; template < simd - type V > constexpr V sub_sat ( const V & x , const V & y ) noexcept ; template < simd - type V > constexpr V mul_sat ( const V & x , const V & y ) noexcept ; template < simd - type V > constexpr V div_sat ( const V & x , const V & y ) noexcept ; template < class U , simd - type V > constexpr rebind_t < U , V > saturate_cast ( const V & v ) noexcept ;
Add the following to the end of the using declarations:
// See [simd.complex.math], simd complex math using datapar :: real ; using datapar :: imag ; using datapar :: arg ; using datapar :: norm ; using datapar :: conj ; using datapar :: proj ; using datapar :: polar ; // See [simd.saturating.math], saturating math functions using datapar :: add_sat ; using datapar :: sub_sat ; using datapar :: mul_sat ; using datapar :: div_sat ; using datapar :: saturate_cast ;
4.3. Add new section [simd.saturating.math]
Add the following section after [simd.complex.math].
saturating math functions [simd.saturating.math]basic_simd template < simd - type V > constexpr V add_sat ( const V & x , const V & y ) noexcept ; template < simd - type V > constexpr V sub_sat ( const V & x , const V & y ) noexcept ; template < simd - type V > constexpr V mul_sat ( const V & x , const V & y ) noexcept ; Constraints:
The type
is a signed or unsigned integer type ([basic.fundamental]).V :: value_type Returns:
A
object where thebasic_simd th element is initialized to the result ofi for allsat - func ( x [ i ], y [ i ]) in the rangei , where sat-func is the corresponding function from [numerics.sat.func].[ 0 , V :: size ()) template < simd - type V > constexpr V div_sat ( const V & x , const V & y ) noexcept ; Constraints:
The type
is a signed or unsigned integer type ([basic.fundamental]).V :: value_type Preconditions:
For every i in the range
,[ 0 , V :: size ()) isy [ i ] != 0 true.Returns:
A
object where thebasic_simd th element is initialized to the result ofi for allsat_div ( x [ i ], y [ i ]) in the rangei .[ 0 , V :: size ()) Remarks:
A function call expression that violates the precondition in the Preconditions element is not a core constant expression ([expr.const]).
template < class U , simd - type V > constexpr rebind_t < U , V > saturate_cast ( const V & v ) noexcept ; Constraints:
The types
andU are signed or unsigned integers ([basic.fundamental]).V :: value_type Returns:
A
object where thebasic_simd th element is initialized to the result ofi for allsaturate_cast ( v [ i ]) in the rangei .[ 0 , V :: size ())