在许多编程语言中,有一个名为floor的函数,它接受一个浮点参数(一个double或一个),并返回不大于参数的最大整数。现在我有一个问题:通过调用floor(a / b)获得不大于a / b的最大整数是否安全?我认为浮点计算可能不准确。因此,例如,a / b的确切值可能是2.999999998,但可以计算为3.00000000,然后floor(a / b)得到3,这是不正确的。
答案 0 :(得分:0)
是的,如果使用IEEE754二进制64格式(即大多数系统上的典型C double
)。一个例子是当a = 0.9
(其中0.9不能用格式表示,实际上是0.90000000000000002220446049250313080847263336181640625)和b = 0.1
(实际上是0.1000000000000000055511151231257827021181583404541015625)时,a/b
的真值实际上是
... 8.999999999999999722444243843710880301531638075180891038116311176859261171872
然而,这将四舍五入到9.0
,因为它比前一个浮点数更接近9
8.9999999999999982236431605997495353221893310546875
所以floor(a/b)
也是9.0
。
答案 1 :(得分:0)
如果a
和b
是整数(小于幂(2,尾数大小+ 2))或者a
和b
是“完全”,那么这是安全的“代表。
START_UPDATE
可以将问题重新表述为:a/b = n
可以a < n*b
吗?
这个论点相当复杂,但这个问题的答案是否定的:
如果a < n*b
则a <= n*b - eps(n*b)/2
。
最糟糕的情况是eps(n*b)/2
接近但小于2的幂。但即使在最坏的情况下,你也有
a < n*b - n*b*eps(1.0)/4
所以
a/b exactly evaluated < n - n*eps(1.0)/4
a/b exactly evaluated < n - eps(n)/4
并且舍入到最近的偶数模式确保
a/b in floating-point < n
END_UPDATE
现在假设a/b
的确切值是一个整数n
,具有向下近似a
和向上近似b
。然后a/b
可以在最后一个位置以超过1/2单位向下近似,因此小于n
。因此a/b = n
而floor(a/b) = n-1
的确切值。
以下C ++代码返回与floor((i/1000)/0.001)
不同的126个i
异常。第一个是0.043/0.001
。
#include <iostream>
#include <cmath>
int main() {
double x, y;
for (int i = 0; i < 1000; ++ i) {
x = i / 1000.0;
y = 0.001;
if (floor(x/y) != (double) i)
std::cout << i << ' ' << floor(x/y) << '\n';
}
std::cout.flush();
}