我想在C ++中计算以下形式的总和
float result = float(x1)/y1+float(x2)/y2+....+float(xn)/yn
xi,yi都是整数。结果将是实际值的近似值。至关重要的是,该近似值小于或等于实际值。我可以假设我的所有价值观都是有限的和积极的。 我尝试使用nextf(,0),就像在此代码段中一样。
cout.precision( 15 );
float a = 1.0f / 3.0f * 10; //3 1/3
float b = 2.0f / 3.0f * 10; //6 2/3
float af = nextafterf( a , 0 );
float bf = nextafterf( b , 0 );
cout << a << endl;
cout << b << endl;
cout << af << endl;
cout << bf << endl;
float sumf = 0.0f;
for ( int i = 1; i <= 3; i++ )
{
sumf = sumf + bf;
}
sumf = sumf + af;
cout << sumf << endl;
正如人们可以看到正确的解决方案是3*6,666... +3.333.. = 23,3333...
但作为输出我得到:
3.33333349227905
6.66666698455811
3.33333325386047
6.66666650772095
23.3333339691162
即使我的小数量小于它们应该代表的数量,它们的总和也不是。在这种情况下,将nextafterf
应用于sumf
会让我23.3333320617676
更小。但这总是有效吗?舍入错误是否可能变得如此之大以至于nextafterf
仍然使我超出了正确的值?
我知道我可以通过实现分数类并精确计算所有内容来避免这种情况。但我很好奇是否有可能用浮动来实现我的目标。
答案 0 :(得分:5)
答案 1 :(得分:2)
我的直接反应是,你采取的方法存在根本缺陷。
问题在于,对于浮点数,nextafter
将采用的步长大小取决于所涉及数字的大小。让我们考虑一个有点极端的例子:
#include <iostream>
#include <iomanip>
#include <cmath>
int main() {
float num = 1.0e-10f;
float denom = 1.0e10f;
std::cout << std::setprecision(7) << num - std::nextafterf(num, 0) << "\n";
std::cout << std::setprecision(7) << denom - std::nextafterf(denom, 0) << "\n";
}
结果:
6.938894e-018
1024
因此,由于分子比分母小很多,因此增量也小得多。
结果似乎相当清楚:结果不是比输入略小,结果应该比输入大一点。
如果你想确保结果小于正确的数字,显而易见的选择是将分子向下,但分母向上(即{{ 1}}。这样,你得到一个更小的分子和一个更大的分母,所以结果总是小于未修改的版本。
答案 2 :(得分:0)
float result = float(x1)/y1+float(x2)/y2+....+float(xn)/yn
有3个可能会出现舍入的地方。
int
转换为float
- 并非总是如此。floating point x/floating point y
floating point quotient + floating point quotient
。通过使用 next (根据公式需要向上或向下),结果肯定会 <精确的数学值。这种方法可能无法生成float
最接近的确切答案,但会接近并且肯定会更小。
float foo(const int *x, const int *y, size_t n) {
float sum = 0.0;
for (size_t i=0; i<n; i++) { // assume x[0] is x1, x[1] is x2 ...
float fx = nextafterf(x[i], 0.0);
float fy = nextafterf(y[i], FLT_MAX);
// divide by slightly smaller over slightly larger
float q = nextafterf(fx / fy, 0.0);
sum = nextafterf(sum + q, 0.0);
}
return sum;
}