当前位置: 首页 > 知识库问答 >
问题:

将单精度除法实现为双精度乘法

鲜于宜修
2023-03-14

对于实现精确 IEEE 754 算术的 C99 编译器,是否存在浮点型的 f除数的值,使得 f / 除数 != (浮点数)(f * (1.0 / 除数)))

编辑:所谓“实现精确的IEEE754算法”,我指的是一个正确地将FLT_EVAL_METHOD定义为0的编译器。

提供符合IEEE 754标准的浮点数的C编译器只能将单精度除法替换为常数,如果所述逆本身可以完全表示为浮点数

实际上,这种情况只发生在2的幂上。因此,程序员Alex可能确信< code>f / 2.0f将被编译为< code>f * 0.5f,但是如果Alex可以乘以< code>0.10f而不是除以10,那么Alex应该通过在程序中编写乘法来表示,或者通过使用诸如GCC的< code>-ffast-math这样的编译器选项来表示。

这个问题是关于将单精度除法转化为双精度乘法的。它总是产生正确的舍入结果吗?有没有可能它更便宜,从而成为编译器可能进行的优化(即使没有< code>-ffast-math)?

我比较了(浮点数)(f*0.10)f/10.0f在1和2之间的f的所有单精度值,没有找到任何反例。这应该涵盖产生正常结果的正常浮点数的所有划分。

然后我用下面的程序将测试推广到所有的除数:

#include <float.h>
#include <math.h>
#include <stdio.h>

int main(void){
  for (float divisor = 1.0; divisor != 2.0; divisor = nextafterf(divisor, 2.0))
    {
      double factor = 1.0 / divisor; // double-precision inverse
      for (float f = 1.0; f != 2.0; f = nextafterf(f, 2.0))
        {
          float cr = f / divisor;
          float opt = f * factor; // double-precision multiplication
          if (cr != opt)
            printf("For divisor=%a, f=%a, f/divisor=%a but (float)(f*factor)=%a\n",
                   divisor, f, cr, opt);
        }
    }
}

搜索空间足够大,足以让这个有趣(246)。程序当前正在运行。在完成之前,有人能告诉我它是否会打印一些东西,也许会解释为什么或为什么不打印?

共有3个答案

谢洛城
2023-03-14

随机搜索得出一个示例。

看起来,当结果是“非正规/亚正规”数时,不等式是可能的。但是,也许我的平台不符合IEEE 754?

f        0x1.7cbff8p-25 
divisor -0x1.839p+116
q       -0x1.f8p-142
q2      -0x1.f6p-142

int MyIsFinite(float f) {
  union {
    float f;
    unsigned char uc[sizeof (float)];
    unsigned long ul;
  } x;
  x.f = f;
  return (x.ul & 0x7F800000L) != 0x7F800000L;
}

float floatRandom() {
  union {
    float f;
    unsigned char uc[sizeof (float)];
  } x;
  do {
    size_t i;
    for (i=0; i<sizeof(x.uc); i++) x.uc[i] = rand();
  } while (!MyIsFinite(x.f));
  return x.f;
}

void testPC() {
  for (;;) {
    volatile float f, divisor, q, qd;
    do {
      f = floatRandom();
      divisor = floatRandom();
      q = f / divisor;
    } while (!MyIsFinite(q));
    qd = (float) (f * (1.0 / divisor));
    if (qd != q) {
      printf("%a %a %a %a\n", f, divisor, q, qd);
      return;
    }
  }

}

Eclipse PC版本:Juno Service Release 2内部版本id:20130225-0426

印曜灿
2023-03-14

如果非默认舍入模式是可能的,这当然是不可能的。例如,在用 3.0f * C 替换 3.0f / 3.0f 时,小于精确倒数的 C 值将在向下或接近零舍入模式下产生错误的结果,而大于精确倒数的 C 值将在向上舍入模式下产生错误的结果。

我不太清楚如果你限制在默认的舍入模式下,你所寻找的是否可能。如果我想出什么,我会考虑一下并修改这个答案。

孔正文
2023-03-14

您的程序不会打印任何内容,假设是舍入-绑扎到偶数舍入模式。该论点的本质如下:

我们假设 f除数都在 1.02.0 之间。因此,对于 [2^23, 2^24) 范围内的某些整数 a 和 b,f = a / 2^23 且除数 = b / 2^23。大小写除数 = 1.0 并不有趣,因此我们可以进一步假设 b

(float)(f * (1.0 / 除数)))可能给出错误结果的唯一方法是使确切的值f /除数非常接近中间情况(即,两个单精度浮点数之间正好中间的数字),以至于表达式f * (1.0 / 除数)中的累积误差将我们从真实值推到该中间情况的另一侧。

但这不可能发生。为了简单起见,让我们首先假设< code>f

因此,f /除数不能足够接近中途情况来产生问题。请注意,f /除数也不可能是精确的中途情况:由于c是奇数,c2 ^ 24是相对质数的,所以我们唯一可以有c / 2 ^ 24 = a / b的方法是如果b2 ^ 24的倍数。但是 b(2^23, 2^24) 的范围内,所以这是不可能的。

f的情况

 类似资料:
  • 问题内容: java中双值的乘法运算符的保证精度是多少? 例如,2.2 * 100是220.00000000000003,但是220是双精度数。220.00000000000003是220之后的下一个两倍。 问题答案: 乘法工作正常,但不能精确表示为双精度。最接近的双打是: 2.199999999999999733(0x4001999999999999) 2.200000000000000177(

  • 我想用一个小数点和一个小数位的格式将任何双精度四舍五入为双精度,这样29575.347434将是2.3。 我试着用decimalFormat做这个,但当我试着我只得到了一个29575.3格式的字符串,带有一个,我不知道如何在保留值为双精度的同时去掉所有小数点。

  • 什么是“精度”?使用float和double时,单和双有什么区别?“单精度32位IEEE 754浮点”“双精度64位IEEE 754浮点”是什么意思?

  • 我很感激每一个暗示,关于转移目标的文件实现。我已经研究了一些关于浮动精度的论文,但在过去的两周里没有取得太大的进展。 提前感谢!

  • 问题内容: 我的同事做了这个实验: 对于这个一年级的操作,我希望得到以下输出: 但是意外的输出是: 为什么在如此简单的操作中double会失败?而且如果double不是这项工作的数据类型,我应该使用什么? 问题答案: 在内部以分数形式存储在 二进制文件中 -例如 值-或值-不能以精确的分数形式存储在二进制中,因此不能存储精确的值,并且相减后的值也不太精确。 如果您关心精确的十进制算术,请使用。 您

  • 问题内容: 当我使用双精度数在Java中将317除以219时,得到1。 例如: 输出为:1。 这是因为它是重复的小数吗?不得不使用BigDecimal代替,这很烦人。 问题答案: 试试这个 java中编码数字的默认类型是,因此使用现有的代码,java正在使用两个数字,并且除法的结果也将是,这将截断小数部分以得出最终结果。然后将此结果从转换为,而没有编译器警告,因为它是 扩大的转换 (保证源类型“适