I perform a subtraction with an unsigned type, say std::uint8_t. I can understand if the result will be obtained by modular arithmetic or not, by means of a comparison:
auto subtract (std::uint8_t x, std::uint8_t y) -> std::pair<std::uint8_t, bool>
{
return {x - y, x < y};
}
My question is the following: Can I avoid the comparison to know if modular arithmetic was in action or not? In other words, does the cpu provide an indication that it happened? Or shall I not worry: Would the compiler somehow optimize the subtraction and comparison operations somehow that the additional comparison won't introduce additional cost?
edt: adapt the example code so that the focus is on the question.
In C++26 (and C23), you can use the standard library function ckd_sub to perform an integer subtraction and determine whether an overflow occurred. This function does not invoke undefined behavior in case of an overflow, even when using signed integers (whereas standard signed integer arithmetic would invoke undefined behavior on overflow).
On most platforms, this function will check a CPU flag after performing the subtraction, in order to detect whether an overflow occurred. That way, a comparison is not necessary in order to detect overflow.
The compilers GCC 15.1 and Clang 18.1 already support this C++26 function when compiling with the command-line option -std=c++26
.
Here is an example implementation of the subtract
function from the question, and a small program which tests it:
#include <iostream>
#include <vector>
#include <utility>
#include <cstdint>
#include <stdckdint.h>
auto subtract( std::uint8_t x, std::uint8_t y ) -> std::pair<std::uint8_t,bool>
{
std::uint8_t result;
bool overflow = ckd_sub( &result, x, y );
return { result, overflow };
}
int main()
{
// define test input
std::vector<std::pair<std::uint8_t,std::uint8_t>> test_input =
{
{ 200, 100 }, // calculate 200 - 100
{ 100, 200 }, // calculate 100 - 200
};
// perform the calculations with the test input
for ( const auto& p : test_input )
{
std::cout <<
"Calculating " <<
static_cast<int>(p.first) <<
" - " <<
static_cast<int>(p.second) <<
'\n';
auto [result,overflow] = subtract( p.first, p.second );
if ( overflow )
{
std::cout << "The operation DID overflow.\n";
}
else
{
std::cout << "The operation DID NOT overflow.\n";
}
std::cout << "The result is: " << static_cast<int>(result) << "\n\n";
}
}
This program has the following output:
Calculating 200 - 100
The operation DID NOT overflow.
The result is: 100
Calculating 100 - 200
The operation DID overflow.
The result is: 156