为 Goldschmidt 部门挑选良好的初步估计

2024-01-03

我正在计算 Q22.10 中的定点倒数戈德施密特师 http://en.wikipedia.org/wiki/Division_(digital)#Goldschmidt_division用于我的 ARM 上的软件光栅器。

只需将分子设置为 1 即可完成此操作,即分子在第一次迭代时变为标量。老实说,我在这里有点盲目地遵循维基百科的算法。文章说,如果分母在半开范围(0.5, 1.0] 内缩放,则可以仅基于分母进行良好的初步估计:设 F 为估计标量,D 为分母,则 F = 2 - D .

但这样做时,我失去了很多精确度。假设我想找到 512.00002f 的倒数。为了缩小数字,我失去了小数部分的 10 位精度,并将其移出。所以,我的问题是:

  • 有没有一种方法可以选择一个不需要标准化的更好的估计?为什么?为什么不?如果能用数学证明为什么这是可能的,那就太好了。
  • 另外,是否可以预先计算第一个估计值,以便序列收敛得更快?目前,平均在第四次迭代后收敛。在 ARM 上,这大约是 50 个周期的最坏情况,并且没有考虑 clz/bsr 的仿真,也没有考虑内存查找。如果可能的话,我想知道这样做是否会增加误差,以及增加多少。

这是我的测试用例。注:软件实现clz第 13 行来自我的帖子here https://stackoverflow.com/questions/376840/trailing-leading-zero-count-for-a-byte/2355682#2355682。如果需要,您可以将其替换为内在函数。clz应返回前导零的数量,对于值 0,应返回 32。

#include <stdio.h>
#include <stdint.h>

const unsigned int BASE = 22ULL;

static unsigned int divfp(unsigned int val, int* iter)
{
  /* Numerator, denominator, estimate scalar and previous denominator */
  unsigned long long N,D,F, DPREV;
  int bitpos;

  *iter = 1;
  D = val;
  /* Get the shift amount + is right-shift, - is left-shift. */
  bitpos = 31 - clz(val) - BASE;
  /* Normalize into the half-range (0.5, 1.0] */
  if(0 < bitpos)
    D >>= bitpos;
  else
    D <<= (-bitpos);

  /* (FNi / FDi) == (FN(i+1) / FD(i+1)) */
  /* F = 2 - D */
  F = (2ULL<<BASE) - D;
  /* N = F for the first iteration, because the numerator is simply 1.
     So don't waste a 64-bit UMULL on a multiply with 1 */
  N = F;
  D = ((unsigned long long)D*F)>>BASE;

  while(1){
    DPREV = D;
    F = (2<<(BASE)) - D;
    D = ((unsigned long long)D*F)>>BASE;
    /* Bail when we get the same value for two denominators in a row.
      This means that the error is too small to make any further progress. */
    if(D == DPREV)
      break;
    N = ((unsigned long long)N*F)>>BASE;
    *iter = *iter + 1;
  }
  if(0 < bitpos)
    N >>= bitpos;
  else
    N <<= (-bitpos);
  return N;
}


int main(int argc, char* argv[])
{
  double fv, fa;
  int iter;
  unsigned int D, result;

  sscanf(argv[1], "%lf", &fv);

  D = fv*(double)(1<<BASE);
  result = divfp(D, &iter); 

  fa = (double)result / (double)(1UL << BASE);
  printf("Value: %8.8lf 1/value: %8.8lf FP value: 0x%.8X\n", fv, fa, result);
  printf("iteration: %d\n",iter);

  return 0;
}

我忍不住花一个小时来解决你的问题......

Jean-Michel Muller 的“Arithmetique des ordinators”(法文)第 5.5.2 节描述了该算法。它实际上是以1为起点的牛顿迭代的特例。本书给出了计算 N/D 的算法的简单公式,其中 D 在 [1/2,1[ 范围内标准化:

e = 1 - D
Q = N
repeat K times:
  Q = Q * (1+e)
  e = e*e

每次迭代时正确位数加倍。在 32 位的情况下,4 次迭代就足够了。您还可以迭代直到e变得太小而无法修改Q.

使用归一化是因为它提供了结果中有效位的最大数量。当输入在已知范围内时,计算所需的误差和迭代次数也更容易。

一旦你的输入值被归一化,你就不需要关心 BASE 的值,直到你得到逆值。您只需将一个 32 位数字 X 归一化在 0x80000000 到 0xFFFFFFFF 范围内,然后计算 Y=2^64/X 的近似值(Y 最多为 2^33)。

这个简化的算法可以为您的 Q22.10 表示实现,如下所示:

// Fixed point inversion
// EB Apr 2010

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

// Number X is represented by integer I: X = I/2^BASE.
// We have (32-BASE) bits in integral part, and BASE bits in fractional part
#define BASE 22
typedef unsigned int uint32;
typedef unsigned long long int uint64;

// Convert FP to/from double (debug)
double toDouble(uint32 fp) { return fp/(double)(1<<BASE); }
uint32 toFP(double x) { return (int)floor(0.5+x*(1<<BASE)); }

// Return inverse of FP
uint32 inverse(uint32 fp)
{
  if (fp == 0) return (uint32)-1; // invalid

  // Shift FP to have the most significant bit set
  int shl = 0; // normalization shift
  uint32 nfp = fp; // normalized FP
  while ( (nfp & 0x80000000) == 0 ) { nfp <<= 1; shl++; } // use "clz" instead

  uint64 q = 0x100000000ULL; // 2^32
  uint64 e = 0x100000000ULL - (uint64)nfp; // 2^32-NFP
  int i;
  for (i=0;i<4;i++) // iterate
    {
      // Both multiplications are actually
      // 32x32 bits truncated to the 32 high bits
      q += (q*e)>>(uint64)32;
      e = (e*e)>>(uint64)32;
      printf("Q=0x%llx E=0x%llx\n",q,e);
    }
  // Here, (Q/2^32) is the inverse of (NFP/2^32).
  // We have 2^31<=NFP<2^32 and 2^32<Q<=2^33
  return (uint32)(q>>(64-2*BASE-shl));
}

int main()
{
  double x = 1.234567;
  uint32 xx = toFP(x);
  uint32 yy = inverse(xx);
  double y = toDouble(yy);

  printf("X=%f Y=%f X*Y=%f\n",x,y,x*y);
  printf("XX=0x%08x YY=0x%08x XX*YY=0x%016llx\n",xx,yy,(uint64)xx*(uint64)yy);
}

如代码中所述,乘法不是完整的 32x32->64 位。 E 将变得越来越小,最初适合 32 位。 Q 将始终为 34 位。我们只取产品的高 32 位。

的推导64-2*BASE-shl留给读者作为练习:-)。如果变成0或负数,则结果无法表示(输入值太小)。

编辑。作为我评论的后续内容,这是第二个版本,Q 上有隐式第 32 位。E 和 Q 现在都存储在 32 位上:

uint32 inverse2(uint32 fp)
{
  if (fp == 0) return (uint32)-1; // invalid

  // Shift FP to have the most significant bit set
  int shl = 0; // normalization shift for FP
  uint32 nfp = fp; // normalized FP
  while ( (nfp & 0x80000000) == 0 ) { nfp <<= 1; shl++; } // use "clz" instead
  int shr = 64-2*BASE-shl; // normalization shift for Q
  if (shr <= 0) return (uint32)-1; // overflow

  uint64 e = 1 + (0xFFFFFFFF ^ nfp); // 2^32-NFP, max value is 2^31
  uint64 q = e; // 2^32 implicit bit, and implicit first iteration
  int i;
  for (i=0;i<3;i++) // iterate
    {
      e = (e*e)>>(uint64)32;
      q += e + ((q*e)>>(uint64)32);
    }
  return (uint32)(q>>shr) + (1<<(32-shr)); // insert implicit bit
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为 Goldschmidt 部门挑选良好的初步估计 的相关文章

随机推荐

  • 如何为 RestController 启用 GZIP? [复制]

    这个问题在这里已经有答案了 我有一个简单的REST控制器使用spring 返回的GZIP响应如何application xml流是否启用 RestController public class MyRest RequestMapping m
  • foreach my $var (@list) -- $var 是一个引用?

    所以 我从来不知道这一点 我想得到一些澄清 我知道如果你这样做 foreach list 如果您在该循环中更改 它将影响实际数据 但是 我不知道如果你这样做 foreach my var1 list 如果您在循环中更改 var1 它将更改实
  • 在 smarty 模板中创建数组? [复制]

    这个问题在这里已经有答案了 我需要从 smarty 模板中的其他一维数组创建一个新数组 那么 在模板文件中创建数组的最佳可能性是什么 谢谢 萨钦 Smarty3 让您 var foo gt bar sub gt 1 2 3 and var
  • Ruby 中 $$ 的含义是什么?

    irb main 002 0 gt gt 5052 是什么意思 在 Ruby 中以及如何 在哪里使用它 is the 进程号 http www opengroup org onlinepubs 9699919799 functions ge
  • HeapTaskDaemon 线程阻塞的 ANR

    我的 Android 应用程序出现 ANR 错误 跟踪显示只有一个线程处于阻塞状态 所有其他线程都处于等待 睡眠 本机状态 因此它似乎并未处于死锁状态 我手动 直接 启动了两个线程 因此我大致知道 ANR 发生在应用程序的哪个部分 不幸的是
  • 从Python文件中读取单个字符?

    我的问题是 除了下面之外 是否还有其他方法可以一次一个字符地遍历文件 with open filename as f while True c f read 1 if not c print End of file break print
  • 使 tkinter 文本小部件适合窗口

    我正在制作一个文本编辑器 其主要小部件是一个文本小部件 供用户实际输入文本 当用户调整窗格大小时 我需要使文本小部件适合窗口 我通过使小部件变大来有点作弊 但这只是一个临时解决方案 让我在寻找解决方案时可以处理其他部分 如何使文本小部件自动
  • 如何在 Rails 2.3.5 中安装/使用 Devise?

    我尝试从 Github 上 Devise 的 v 1 2 oauth 分支进行安装 但仍然出现错误 如何在 Rails 2 3 5 应用程序上安装 devise gem 我特别想要一个可以与omniauth一起使用的 gem install
  • Mac App Store:放弃 32 位支持转而支持 ARC,32 位版本的现有用户会看到更新消息吗?

    我正在考虑放弃 32 位支持 转而支持自动引用计数 仅支持 64 位二进制文 件 我想在 Mac App Store 中避免出现这两种情况 For a 旧 32 位 Mac 用户 谁购买了支持 32 位的先前版本 他们会在 Mac App
  • Python 中是否有用于纯文本文件的本机模板系统?

    我正在寻找用于将输出格式化为简单文本的 Python 技术或模板系统 我需要的是它将能够迭代多个列表或字典 如果我能够将模板定义到单独的文件 如output templ 中而不是将其硬编码到源代码中 那就太好了 作为我想要实现的简单示例 我
  • 如何从9GAG获取数据json

    也许你认为这是一个愚蠢的问题 但我希望你能给我一些建议 我的问题 当我查看 9gag com 的源代码时 我意识到他们有一些行代码来加载更多内容 div class loading a class btn badge load more p
  • PyYAML 中的数组没有缩进或空格

    在下面的代码中我创建了net plan dict变量字典并将其转换为YAML格式文件 在字典里我有一个叫做addresses这是一个由三个元素组成的数组 创建YAML文件后 这三个数组元素没有放置在addresses field impor
  • JPA针对不同数据库的不同列类型

    是否可以根据使用的数据库使用 JPA 定义不同的列类型 我需要将 id 存储为 uuid 并且它必须是可移植的 那就是问题所在 PostgreSQL有 uuid MSSQL有 uniqueidentifier 而Oracle什么都没有 我想
  • android中textview的圆角

    我有一个文本视图 希望它的角是圆形的 我已经知道可以使用android background drawable somefile 就我而言 该标签已包含在内 因此无法再次使用 例如android background drawable my
  • Rails 更改 form_for 中提交的路由

    我有一个模型 文章 和一个嵌套在文章中的模型 评级 文章 123 评级 我想更改 ratings form html erb 中 f submit 的路由 现在是这样 按提交后 我的申请路由到 评分 111 但我想将其路由到 文章 123
  • WCF 服务应该返回 EntityObject 还是 POCO/DTO 类?

    我一直在查看很多使用 EntityFramework 的 WCF 示例 其中大多数似乎都会向客户端返回某种 POCO 或 DTO 类 我想知道为什么这是默认的EntityObject包括 DataContract 属性和工具INotifyP
  • Angula2 Karma 无法加载“webpack”!

    我已经在 Angular2 项目 Webpack Karma 上工作了几个月 该项目基于此入门程序的稍旧版本 https github com preboot angular2 webpack https github com preboo
  • 带注入的定制 Serilog 水槽?

    我创建了一个简单的 Serilog 接收器项目 如下所示 namespace MyApp Cloud Serilog MQSink public class MessageQueueSink ILogEventSink private re
  • 无法使用@Value在Spring应用程序中获取maven project.version属性

    如何使用 Value注释在Spring Boot应用程序中获取maven project version属性 经过一些关于如何在 SpringBoot 应用程序中获取 Maven 项目版本的研究和试验后 我找不到任何适合我的东西 由于类加载
  • 为 Goldschmidt 部门挑选良好的初步估计

    我正在计算 Q22 10 中的定点倒数戈德施密特师 http en wikipedia org wiki Division digital Goldschmidt division用于我的 ARM 上的软件光栅器 只需将分子设置为 1 即可