Why is std::abs(9484282305798401ull) = 9484282305798400?
Why is std::abs(9484282305798401ull) = 9484282305798400?
我目前正在编写一个模板化的帮助程序方法,该方法可以将一般的 C 数(包括无符号长长)转换为 GMP 库中的mpz_class数。在这两者之间,有一个呼唤std::abs
.
然而,事实证明,对于 C++17 (g++ 6.3.1),
#include <iostream>
#include <cmath>
int main()
{
std::cout << (unsigned long long)std::abs(9484282305798401ull);
}
给出不正确的9484282305798400
输出。
正如我从cmath中了解到的那样,std::abs
首先将论点转换为双精度。
根据C++文档,double 有 52 个尾数位,这意味着 I 必须严格小于2^52 = 4503599627370496
的最大整数值才会有任何精度损失。
我说9484282305798401
超过这个限制,std::abs
最终会放弃精度给出不正确的答案,这是否正确?
澄清一下,我绝对知道要求无符号整数的绝对值是完全没有意义的;但是,我希望模板化函数适用于通用 C 数字,而不必分别为每个有符号和无符号类型专门创建专用化。
您的程序格式不正确。来自 [c.math.abs]/29.9.2.3:
如果使用
true
is_unsigned_v<X>
的X
类型的参数调用abs()
,并且无法通过积分提升将X
转换为int
,则程序格式不正确。
不过,编译器应该警告您这一点。
无论如何,在无符号类型上调用std::abs
也没有意义。
首先,你正在做的事情在上下文之外并没有真正的意义(获取无符号类型的绝对值)。但我离题了。
您发布的代码无法编译。至少在我使用的编译器中没有(无论 repl.it 使用哪个)。相反,它抱怨模棱两可的过载。即使它确实编译了,它也会将unsigned long long
转换为无法支持其实际值的另一种类型(在本例中为double
)。
将abs
更改为llabs
如下所示:
std::cout << (unsigned long long)std::llabs(9484282305798401ull);
..两者都使它编译并产生准确的结果。请参阅此处整数类型的不同abs
函数的文档。
如果要以不同于标准库函数的方式管理无符号类型,则可以创建自己的abs
重载:
#include <cmath>
#include <type_traits>
namespace my {
template <class S>
auto abs (S x) -> typename std::enable_if<std::is_signed<S>::value,
decltype(std::abs(x))>::type
{
return std::abs(x);
}
template <class U>
auto abs (U x) -> typename std::enable_if<std::is_unsigned<U>::value, U>::type
{
return x;
}
} // namespace my
然后
std::cout << my::abs(9484282305798401ull) << 'n' // -> 9484282305798401
<< my::abs(-3.14159) << 'n' // -> 3.14159
<< my::abs(std::numeric_limits<char>::min()) << 'n' // -> 128
<< my::abs(std::numeric_limits<int>::min()) << 'n' // -> -2147483648
请注意,std::abs
提升了char
s(在我的实现中签名),但由于 2 的补码表示int
s,无法检索 INT_MIN 的绝对值。
- 没有找到相关文章