在 C++14 中将无符号值转换为其有符号否定的便携式方法



我正在寻找一个可移植的C++函数,它计算数学函数f(x( = -x,其中输入是无符号的,输出是有符号的。 该函数应适用于整个long long值范围,如果输入超出范围,则引发异常。 它不应该依赖于编译器对更大整数类型的支持来捕获溢出。 这里有两个工作但不需要的函数来计算我想要的东西:

#include <cerrno>
#include <limits>
#include <stdexcept>
using namespace std;
long long
slow_negate(unsigned long long val)
{
string s = '-' + to_string(val);
errno = 0;
long long r = strtoll(s.c_str(), nullptr, 10);
if (errno)
throw range_error("slow_convert overflow");
return r;
}
constexpr long long
twos_complement_negate(unsigned long long val)
{
constexpr long long minll = numeric_limits<long long>::min(),
maxll = numeric_limits<long long>::max();
static_assert(minll == ~maxll && -(minll + 1) == maxll,
"only works for two's complement arithmetic");
return val > maxll + 1U ? throw range_error("twos_complement_negate overflow")
: val >= 0 ? (unsigned long long) -val
: (unsigned long long) (-val + 1) + 1;
}

这里的问题是,第一个函数显然非常慢,而不是一个constexpr(这样一个简单的函数确实应该是这样(,而第二个函数对两个补码整数表示做出了额外的假设,这些假设不是语言规范所保证的。

这听起来像是一个简单的问题,但不知何故,我一直无法想出一个好的便携式解决方案,适用于所有可能的输入。

我错过了什么,或者你为什么不能做

constexpr signed long long f(unsigned long long input) 
{
return (input > static_cast<unsigned long long>(
std::numeric_limits<signed long long>::max()))
? throw std::range_error{ "overflow" } 
: -static_cast<signed long long>(input);
}

或者,更一般

template<typename T>
constexpr auto f(T input) -> std::make_signed_t<T>
{
using signed_t = std::make_signed_t<T>;
return (input > static_cast<T>(std::numeric_limits<signed_t>::max()))
? throw std::range_error{ "overflow" } 
: -static_cast<signed_t>(input);
}

最新更新