首页 > 解决方案 > 查明从十进制到二进制浮点的转换是否准确、向上舍入或向下舍入

问题描述

我知道可以用十进制浮点数精确表示的值通常无法用二进制浮点数表示为精确值。在例如 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 作为示例 - 我更喜欢语言不可知的解决方案。

标签: floating-pointfloating-point-conversion

解决方案


对于某些语言,在选择条件下,代码可以通过最接近、向上或向下(或向 0,或...)的转换来控制舍入模式。

然后可以将默认(通常到最近)的转换结果与updown进行比较。

#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

推荐阅读