Floating point 确定从十进制到二进制浮点的转换是精确的、向上舍入的还是向下舍入的

Floating point 确定从十进制到二进制浮点的转换是精确的、向上舍入的还是向下舍入的,floating-point,floating-point-conversion,Floating Point,Floating Point Conversion,我知道可以用十进制浮点数精确表示的值通常不能用二进制浮点数精确表示。它很容易用python等语言演示 a = float("0.5") print('%.17e' % (a)) 5.00000000000000000e-01 这是完全可以预料到的,也是完全正确的。但我想知道的是,是否有一种简单的方法可以确定转换后的值是高于还是低于精确值(或者它是否精确转换)。从上面的例子中,我显然可以对输出字符串做一些事情,但这似乎非常笨拙,而且知道这一点非常有用(例如,如果在循环中递增一

我知道可以用十进制浮点数精确表示的值通常不能用二进制浮点数精确表示。它很容易用python等语言演示

a = float("0.5")
print('%.17e' % (a))
5.00000000000000000e-01

这是完全可以预料到的,也是完全正确的。但我想知道的是,是否有一种简单的方法可以确定转换后的值是高于还是低于精确值(或者它是否精确转换)。从上面的例子中,我显然可以对输出字符串做一些事情,但这似乎非常笨拙,而且知道这一点非常有用(例如,如果在循环中递增一个浮点数,您可以使用它来决定是否要获得预期的迭代次数)我希望有一种更直接的方法来做到这一点。这里我只是以python为例——我更喜欢一种与语言无关的解决方案。

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

然后可以将默认值(通常为最接近值)的转换结果与向上或向下进行比较


那真的很有帮助。事实上,如果能够指定“向上”或“向下”而不是“最近的”,那么我的示例用例就可以进行排序了。@BillSellers:您的示例用例不起作用。请参阅我对这个问题的评论和反例。@BillSellers关于“决定是否获得预期的迭代次数”:和
0≤ x<0.036by.001
,也许最好使用整数进行迭代:
long n=lrund(0.036/0.001);对于(i=0;iIt,在舍入时具有方向性仍然很好,因为即使使用整数计数,我事先知道我可以接近我的极限,但不会超过它,这是我想要的行为。同样,在其他情况下,我可能希望保证超过极限。总之,拥有该选项非常有用。Re“如果你在循环中增加一个浮点数,你可以用它来决定你是否会得到预期的迭代次数?”:不,你不能。例如,考虑通过0递增。≤ 使用IEEE-754二进制文件32..001将x<0.036 x.001转换为0.001000000047497451305389404296875,.036转换为0.03599998450279235858984375,.001+大于0.036的36倍−, 因此,您最多可以期望36次迭代。但是有37次,因为许多中间舍入恰好向下舍入,所以第36次和是0.035999472498893779296875。但是如果我向下舍入增量并向上舍入最终目标,这仍然是一个问题吗?我可以看到,如果我将它们都向下舍入,那么可能会出现这样的情况sn不起作用。我还可以看到,当增量非常小且阈值非常大时,会出现边缘情况,但如果我最终进入该区域,则所有赌注都将被取消!Re“但是如果我将增量向下取整,并将最终目标向上取整,这仍然是一个问题吗?”:是。在binary32中,从0到.27825乘以.00001的增量有27824次迭代。通常,您无法预测中间舍入误差将如何累积。即使增量太高(或太小),舍入误差也可能使总和太小(或太高)。通过浮点值递增的常用方法是使用整数值递增,并将计数器缩放到所需的范围,因此不会累积舍入误差。例如,在C中,类似于(int i=0;i
a = float("0.054")
print('%.17e' % (a))
5.39999999999999994e-02
a = float("0.055")
print('%.17e' % (a))
5.50000000000000003e-02
#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"));
}
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