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

Yiy*_*Lee 7 c++ floating-point precision c++11 c++17

我正在编写一个模板化辅助方法,它可以将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);
}
Run Code Online (Sandbox Code Playgroud)

给出不正确的输出9484282305798400.

正如我从cmath所理解的那样,std::abs首先将参数转换为双精度.

根据C++文档,double有52个尾数位,这意味着我必须严格小于2^52 = 4503599627370496任何精度损失之前的最大整数值.

我是否正确地说,由于9484282305798401超过此限制,std::abs最终会丢弃精确度以给出错误的答案?

为了澄清,我完全清楚,要求无符号整数的绝对值是完全没有意义的.但是,我希望模板化函数适用于一般C编号,而不必分别为每个有符号和无符号类型专门创建特殊化.

Rak*_*111 8

你的程序格式不正确.从[c.math.abs] /29.9.2.3开始:

如果abs()使用类型的参数调用,如果无法通过整数提升转换X为该类型,is_­unsigned_­v<X>则该程序格式错误.trueXint

编译器应该警告你这一点.

std::abs无论如何,调用无符号类型也没有意义.


sty*_*ybl 5

首先,你正在做的事情在上下文中没有意义(获得无符号类型的绝对值).但我离题了.

您发布的代码无法编译.至少不在我使用的编译器中(无论哪个repl.it使用).相反,它抱怨模糊的过载.即使它确实编译了,也会将其unsigned long long转换为不能支持其实际值的不同类型(在本例中double).

更改absllabs像这样:

std::cout << (unsigned long long)std::llabs(9484282305798401ull);
Run Code Online (Sandbox Code Playgroud)

..使它编译并产生准确的结果.请在此处abs查看整数类型的不同函数的文档.