确保浮动小于精确值

时间:2015-08-19 12:47:43

标签: c++ floating-point rounding precision floating-point-precision

我想在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仍然使我超出了正确的值?

我知道我可以通过实现分数类并精确计算所有内容来避免这种情况。但我很好奇是否有可能用浮动来实现我的目标。

3 个答案:

答案 0 :(得分:5)

尝试将浮动舍入模式更改为FE_TOWARDZERO。

请参阅此处的代码示例:

Change floating point rounding mode

答案 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个可能会出现舍入的地方。

  1. int转换为float - 并非总是如此。
  2. 分部floating point x/floating point y
  3. 添加:floating point quotient + floating point quotient
  4. 通过使用 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;
    }