确定从十进制到二进制浮点数的转换是精确的、向上舍入的还是向下舍入的
Finding out whether conversion from decimal to binary floating point is exact, rounding up, or rounding down
我知道可以用十进制浮点数精确表示的值往往不能用二进制浮点数精确表示。在例如中很容易证明python
a = float("0.5")
print('%.17e' % (a))
5.00000000000000000e-01
a = float("0.054")
print('%.17e' % (a))
5.39999999999999994e-02
a = float("0.055")
print('%.17e' % (a))
5.50000000000000003e-02
这完全是意料之中的,也是非常恰当的。但我想知道的是,是否有一种简单的方法可以确定转换后的值是高于还是低于确切值(或者实际上是否准确转换)。从上面的示例中,我显然可以对输出字符串做一些事情,但这看起来非常笨拙,而且了解它是一件非常有用的事情(例如,如果你在循环中递增一个浮点数,你可以用它来决定你是否是否会得到预期的迭代次数)我希望有一种更直接的方法来做到这一点。我在这里仅使用 python 作为示例 - 我更喜欢与语言无关的解决方案。
对于某些语言,在 select 条件下,代码可以通过向最近、向上或向下(或朝向 0,或...)的转换来控制舍入模式。
然后可以将默认(通常到最近的)的转换结果与up或down[进行比较=21=].
#include <fenv.h>
#include <assert.h>
#include <stdio.h>
double string_to_double(int round_dir, const char *s) {
#pragma STDC FENV_ACCESS ON
int save_round = fegetround();
int setround_ok = fesetround(round_dir);
assert(setround_ok == 0);
char *endptr;
double d = strtod(s, &endptr);
fesetround(save_round);
return d;
}
// Return 1: up, -1: down, 0: exact
int updn(const char *s) {
double up = string_to_double(FE_UPWARD, s);
double nr = string_to_double(FE_TONEAREST, s);
double dn = string_to_double(FE_DOWNWARD, s);
// Others modes: FE_TOWARDZERO
printf("%.17e, %.17e, %.17e, ", dn, nr, up);
if (up == dn) {
assert(up == nr);
return 0;
}
if (up > nr) return -1;
if (dn < nr) return 1;
return 0; // Unexpected, unless NaN
}
int main() {
printf("%2d\n", updn("0.5"));
printf("%2d\n", updn("0.054"));
printf("%2d\n", updn("0.055"));
}
输出:1:向上,-1:向下,0:准确
5.00000000000000000e-01, 5.00000000000000000e-01, 5.00000000000000000e-01, 0
5.39999999999999994e-02, 5.39999999999999994e-02, 5.40000000000000063e-02, -1
5.49999999999999933e-02, 5.50000000000000003e-02, 5.50000000000000003e-02, 1
我知道可以用十进制浮点数精确表示的值往往不能用二进制浮点数精确表示。在例如中很容易证明python
a = float("0.5")
print('%.17e' % (a))
5.00000000000000000e-01
a = float("0.054")
print('%.17e' % (a))
5.39999999999999994e-02
a = float("0.055")
print('%.17e' % (a))
5.50000000000000003e-02
这完全是意料之中的,也是非常恰当的。但我想知道的是,是否有一种简单的方法可以确定转换后的值是高于还是低于确切值(或者实际上是否准确转换)。从上面的示例中,我显然可以对输出字符串做一些事情,但这看起来非常笨拙,而且了解它是一件非常有用的事情(例如,如果你在循环中递增一个浮点数,你可以用它来决定你是否是否会得到预期的迭代次数)我希望有一种更直接的方法来做到这一点。我在这里仅使用 python 作为示例 - 我更喜欢与语言无关的解决方案。
对于某些语言,在 select 条件下,代码可以通过向最近、向上或向下(或朝向 0,或...)的转换来控制舍入模式。
然后可以将默认(通常到最近的)的转换结果与up或down[进行比较=21=].
#include <fenv.h>
#include <assert.h>
#include <stdio.h>
double string_to_double(int round_dir, const char *s) {
#pragma STDC FENV_ACCESS ON
int save_round = fegetround();
int setround_ok = fesetround(round_dir);
assert(setround_ok == 0);
char *endptr;
double d = strtod(s, &endptr);
fesetround(save_round);
return d;
}
// Return 1: up, -1: down, 0: exact
int updn(const char *s) {
double up = string_to_double(FE_UPWARD, s);
double nr = string_to_double(FE_TONEAREST, s);
double dn = string_to_double(FE_DOWNWARD, s);
// Others modes: FE_TOWARDZERO
printf("%.17e, %.17e, %.17e, ", dn, nr, up);
if (up == dn) {
assert(up == nr);
return 0;
}
if (up > nr) return -1;
if (dn < nr) return 1;
return 0; // Unexpected, unless NaN
}
int main() {
printf("%2d\n", updn("0.5"));
printf("%2d\n", updn("0.054"));
printf("%2d\n", updn("0.055"));
}
输出:1:向上,-1:向下,0:准确
5.00000000000000000e-01, 5.00000000000000000e-01, 5.00000000000000000e-01, 0
5.39999999999999994e-02, 5.39999999999999994e-02, 5.40000000000000063e-02, -1
5.49999999999999933e-02, 5.50000000000000003e-02, 5.50000000000000003e-02, 1