[英]Why is std::abs(9484282305798401ull) = 9484282305798400?
我正在编写一个模板化辅助方法,它可以将C编号(包括unsigned long long)转换为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
首先将参数转换为double。
根据C ++文档,double有52个尾数位,这意味着在丢失任何精度之前,我必须严格小于2^52 = 4503599627370496
的最大整数值。
我是否正确地说,因为9484282305798401
超过此限制, std::abs
最终会丢弃精确度以给出错误答案?
为了澄清,我完全清楚,要求无符号整数的绝对值是完全没有意义的。 但是,我希望模板化函数适用于一般C编号,而不必分别为每个有符号和无符号类型专门创建特殊化。
你的程序格式不正确。 从[c.math.abs] /29.9.2.3开始 :
如果使用类型
X
的参数调用abs()
,其中is_unsigned_v<X>
为true
并且如果X
不能通过整数提升转换为int
,则程序is_unsigned_v<X>
。
编译器应该警告你这一点。
无论如何,在无符号类型上调用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
(在我的实现中签名),但是由于int
的2的补码表示,无法检索INT_MIN的绝对值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.