我记得在某处读过一个例子,它说将双精度/浮点值转换为整数可能会导致错误,因为精度损失。例如
int a = 5.0000;
不能保证 a 等于 5,因为实际上 5.0000 可以存储为 4.9999 或其他东西......
我想知道使用ceil,地板和圆形是否会遇到类似的问题。例如,请考虑以下代码。
double a = ***; //some double-precision value
int b = ceil(a);
long c = floor(a);
long long d = round(a);
如果 a 的值在整数值范围内,b、c、d 的值是否保证正确?
更新:
感谢您的友好回答。基于这些答案,我发布了一个后续问题。
ceil、地板和圆形函数放大精度损失问题的严重程度如何,以及如何消除这些影响?
实际上
5.0000
可以存储为4.9999
IEEE浮点数(简称"floats",我不是指float
C++数据类型)的情况并非如此。5.0
完全可以表示为 IEEE 浮点数,所有适合尾数的整数也是如此(即具有
对于大小大于尾数大小的值,IEEE浮点数根本没有分数分量(但相邻浮点数之间的距离可能大于1)。例如,在尾数小于 100 位的 IEEE 浮点数中,最接近pow(2, 100) + 1
的可表示浮点数pow(2, 100)
(例如常见的 64 位和 32 位浮点数)。round
,ceil
和floor
当然会返回pow(2, 100)
(这不适合常见的整数类型,可能会导致错误)。
此外,ceil
、round
和floor
不会引入错误,但它们可能会放大现有的错误。5.0
是完全可表示的,但计算1.1 + 3.9
可能不会产生5.0
,因为1.1
和3.9
不完全可表示。在这种情况下,这些函数可能会(正确)从指定到的5
舍入,这可能不是您的意图。我怀疑这就是你引用的材料所谈论的,也是你感到困惑的:"精度损失"发生在四舍五入之前,随后的四舍五入放大了这个错误(但实际上并没有错误地舍入)。
也就是说,C++不能保证IEEE浮点格式(它只是迄今为止最常见的一种)。实现可能使用完全不同的格式。
floor
、ceil
和round
是精确的运算。
但是,您的代码使用其中一个函数,后跟整数强制转换。整数转换不是精确操作,因为并非所有整数值双精度都可以表示为实际整数。
但是,给定您指定的约束,前提是 a 的值在整数值范围内,则强制转换也将是准确的。
但是(这是很多但是)首先没有什么可以修复不准确的数据。如果您的变量的值为 4.99999999,但正确的值是 5.0,则调用floor
将给出不正确的值 4。
是的,他们会的。
floor
和ceil
无法补偿并非所有整数都是 可代表。考虑:
float f = 16'777'217.5f;
cout << setprecision(20);
cout << ceil(f) << endl;
cout << floor(f) << endl;
它将为两者打印16777218
,如果float
是 32 位 IEEE 754
见 http://coliru.stacked-crooked.com/a/894c8cb0ee517b16