繁体   English   中英

为什么std :: abs(9484282305798401ull)= 9484282305798400?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM