确保浮点数小于精确值
Ensure float to be smaller than exact value
我想在 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
仍然使我高于正确值?
我知道我可以通过对分数实施 class 并准确计算所有内容来避免这种情况。但是我很好奇是否可以用花车实现我的目标。
尝试将浮动舍入模式更改为 FE_TOWARDZERO。
参见此处的代码示例:
Change floating point rounding mode
我的第一反应是您采用的方法存在根本性缺陷。
问题在于,对于浮点数,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
所以,由于分子比分母小很多,增量也小很多。
结果似乎很清楚:结果应该比输入大很多,而不是结果比输入小一点。
如果要确保结果小于正确的数字,显而易见的选择是将分子向下舍入,但分母向上舍入(即 nextafterf(denom, positive_infinity)
。这样,您会得到更小的分子和更大的分母,因此结果总是小于未修改的版本。
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,(根据方程需要向上或向下),结果肯定会 less数学值。这种方法可能不会生成 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;
}
我想在 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
仍然使我高于正确值?
我知道我可以通过对分数实施 class 并准确计算所有内容来避免这种情况。但是我很好奇是否可以用花车实现我的目标。
尝试将浮动舍入模式更改为 FE_TOWARDZERO。
参见此处的代码示例:
Change floating point rounding mode
我的第一反应是您采用的方法存在根本性缺陷。
问题在于,对于浮点数,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
所以,由于分子比分母小很多,增量也小很多。
结果似乎很清楚:结果应该比输入大很多,而不是结果比输入小一点。
如果要确保结果小于正确的数字,显而易见的选择是将分子向下舍入,但分母向上舍入(即 nextafterf(denom, positive_infinity)
。这样,您会得到更小的分子和更大的分母,因此结果总是小于未修改的版本。
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,(根据方程需要向上或向下),结果肯定会 less数学值。这种方法可能不会生成 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;
}