仅使用单精度浮点近似 [0,pi] 上的余弦

2024-02-20

我目前正在研究余弦的近似值。由于最终的目标设备是自行开发的 32 位浮点 ALU / LU,并且有专门的 C 编译器,因此我无法使用 C 库数学函数(cosf,...)。我的目标是编写在准确性和指令/周期数量方面有所不同的各种方法。

我已经尝试过很多不同的近似算法,从 fdlibm、泰勒展开、pade 近似、使用 maple 的 remez 算法等等开始......

但是,一旦我仅使用浮点精度来实现它们,就会出现显着的精度损失。并且可以肯定的是:我知道使用双精度,更高的精度根本没有问题......

现在,我有一些近似值,精确到 pi/2 附近的几千 ulp(最大误差发生的范围),并且我觉得我受到单精度转换的限制。

为了解决主题参数减少:输入以弧度为单位。我假设参数减少会因除法/乘法而导致更多的精度损失....由于我的整体输入范围只有 0..pi,我决定将参数减少到 0..pi/2。

因此我的问题是:有人知道余弦函数的高精度单精度近似(在最好的情况下效率很高)吗?是否有任何算法可以优化单精度的近似值?您知道内置 cosf 函数内部是否计算单精度或双精度的值吗? ~

float ua_cos_v2(float x)
{
    float output;
    float myPi = 3.1415927410125732421875f;
    if (x < 0) x = -x;
    int quad = (int32_t)(x*0.63661977236f);//quad = x/(pi/2) = x*2/pi
    if (x<1.58f && x> 1.57f) //exclude approximation around pi/2
    {
        output = -(x - 1.57079637050628662109375f) - 2.0e-12f*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f) + 0.16666667163372039794921875f*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f) + 2.0e-13f*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)+ 0.000198412701138295233249664306640625f*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f);
        output -= 4.37E-08f;
    }
    else {
        float param_x;
        int param_quad = -1;
        switch (quad)
        {
        case 0:
            param_x = x;
            break;
        case 1:
            param_x = myPi - x;
            param_quad = 1;
            break;
        case 2:
            param_x = x - myPi;
            break;
        case 3:
            param_x = 2 * myPi - x;
            break;
        }
        float c1 = 1.0f,
            c2 = -0.5f,
            c3 = 0.0416666679084300994873046875f,
            c4 = -0.001388888922519981861114501953125f,
            c5 = 0.00002480158218531869351863861083984375f,
            c6 = -2.75569362884198199026286602020263671875E-7f,
            c7 = 2.08583283978214240050874650478363037109375E-9f,
            c8 = -1.10807162057025010426514199934899806976318359375E-11f;
        float _x2 = param_x * param_x;
        output = c1 + _x2*(c2 + _x2*(c3 + _x2*(c4 + _x2*(c5 + _x2*(c6 + _x2*(c7 
        + _x2* c8))))));
        if (param_quad == 1 || param_quad == 0)
            output = -output;
    }
    return output;
}

~

如果我忘记任何信息,请随时询问!

提前致谢


仅使用本机精度运算当然可以计算 [0, π] 上的余弦,任何所需的误差范围 >= 0.5 ulp。然而,目标越接近正确舍入的函数,就越需要更多的前期设计工作和运行时的计算工作。

超越函数的实现通常包括参数减少、核心近似、抵消参数减少的最终修复。在参数减少涉及减法的情况下,需要通过显式或隐式使用更高的精度来避免灾难性抵消。隐式技术可以设计为仅依赖于本机精度计算,例如通过将像 π 这样的常数拆分为未计算的总和,例如1.57079637e+0f - 4.37113883e-8f使用 IEEE-754 时binary32(单精度)。

当硬件提供融合乘加 (FMA) 运算时,通过本机精度计算实现高精度会容易得多。 OP 没有指定他们的目标平台是否提供此操作,因此我将首先展示一种非常简单的方法,仅依靠乘法和加法提供中等精度(最大误差 float映射到 IEEE-754binary32 format.

以下内容基于存档的博客文章 https://archive.ph/VyyYh作者:Colin Wallace,标题为“用切比雪夫多项式将 sin(x) 近似为 5 ULP”。它建议通过使用 sin(x)/(x*(x²-π²)) 的 x² 多项式来近似 [-π, π] 上的正弦,然后将其乘以 x*(x²-π²)。更准确地计算 a2-b2 的标准技巧是将其重写为 (a-b) * (a+b)。将 π 表示为两个浮点数 pi_high 和 pi_low 的未计算和,可以避免减法过程中发生灾难性抵消,从而将计算 x²-π² 变为((x - pi_hi) - pi_lo) * ((x + pi_hi) + pi_lo).

理想情况下,多项式核心近似应使用极小极大近似,其中min形象化了max我的错误。我在这里已经这样做了。可以使用 Maple 或 Mathematics 等各种标准工具来实现此目的,或者基于 Remez 算法创建自己的代码。

对于 [0, PI] 上的余弦计算,我们可以利用 cos (t) = sin (π/2 - t) 这一事实。将 x = (π/2 - t) 代入 x * (x - π/2) * (x + π/2) 得到 (π/2 - t) * (3π/2 - t) * (-π/2 -t)。与以前一样,常量可以分为高部分和低部分(或头部分和尾部分,使用另一种常见的习惯用法)。

/* Approximate cosine on [0, PI] with maximum error of 5.081154 ulp */
float cosine (float x)
{
    const float half_pi_hi       =  1.57079637e+0f; //  0x1.921fb6p+0
    const float half_pi_lo       = -4.37113883e-8f; // -0x1.777a5cp-25
    const float three_half_pi_hi =  4.71238899e+0f; //  0x1.2d97c8p+2
    const float three_half_pi_lo = -1.19248806e-8f; // -0x1.99bc5cp-27
    float p, s, hpmx, thpmx, nhpmx;

    /* cos(x) = sin (pi/2 - x) = sin (hpmx) */
    hpmx = (half_pi_hi - x) + half_pi_lo;               // pi/2 - x
    thpmx = (three_half_pi_hi - x) + three_half_pi_lo;  // 3*pi/2 - x
    nhpmx = (-half_pi_hi - x) - half_pi_lo;             // -pi/2 - x

    /* P(hpmx*hpmx) ~= sin (hpmx) / (hpmx * (hpmx * hpmx - pi * pi)) */
    s = hpmx * hpmx;
    p =         1.32823530e-10f;//  0x1.241500p-33
    p = p * s - 2.33173445e-8f; // -0x1.9096c4p-26 
    p = p * s + 2.52237896e-6f; //  0x1.528c48p-19
    p = p * s - 1.73501656e-4f; // -0x1.6bdbfep-13
    p = p * s + 6.62087509e-3f; //  0x1.b1e7dap-8
    p = p * s - 1.01321183e-1f; // -0x1.9f02f6p-4
    return hpmx * nhpmx * thpmx * p;
}

下面我展示了一种经典方法,它首先将参数减少为 [-π/4, π/4],同时记录象限。然后,象限告诉我们是否需要在此主要近似区间上计算正弦或余弦的多项式近似,以及是否需要翻转最终结果的符号。此代码假设目标平台支持 IEEE-754 指定的 FMA 操作,并且它是通过标准 C 函数映射的fmaf()对于单精度。

该代码很简单,除了使用舍入模式到最近或偶数的浮点到整数转换来计算象限,该转换是通过“幻数加法”方法执行的,并与 2/ 的乘法相结合π(相当于除以 π/2)。最大误差小于1.5 ulps。

/* compute cosine on [0, PI] with maximum error of 1.429027 ulp */
float my_cosf (float a)
{
    const float half_pi_hi =  1.57079637e+0f; //  0x1.921fb6p+0
    const float half_pi_lo = -4.37113883e-8f; // -0x1.777a5cp-25
    float c, j, r, s, sa, t;
    int i;

    /* subtract closest multiple of pi/2 giving reduced argument and quadrant */
    j = fmaf (a, 6.36619747e-1f, 12582912.f) - 12582912.f; // 2/pi, 1.5 * 2**23
    a = fmaf (j, -half_pi_hi, a);
    a = fmaf (j, -half_pi_lo, a);

    /* phase shift of pi/2 (one quadrant) for cosine */
    i = (int)j;
    i = i + 1;

    sa = a * a;
    /* Approximate cosine on [-PI/4,+PI/4] with maximum error of 0.87444 ulp */
    c =               2.44677067e-5f;  //  0x1.9a8000p-16
    c = fmaf (c, sa, -1.38877297e-3f); // -0x1.6c0efap-10
    c = fmaf (c, sa,  4.16666567e-2f); //  0x1.555550p-5
    c = fmaf (c, sa, -5.00000000e-1f); // -0x1.000000p-1
    c = fmaf (c, sa,  1.00000000e+0f); //  1.00000000p+0
    /* Approximate sine on [-PI/4,+PI/4] with maximum error of 0.64196 ulp */
    s =               2.86567956e-6f;  //  0x1.80a000p-19
    s = fmaf (s, sa, -1.98559923e-4f); // -0x1.a0690cp-13
    s = fmaf (s, sa,  8.33338592e-3f); //  0x1.111182p-7
    s = fmaf (s, sa, -1.66666672e-1f); // -0x1.555556p-3
    t = a * sa;
    s = fmaf (s, t, a);

    /* select sine approximation or cosine approximation based on quadrant */
    r = (i & 1) ? c : s;
    /* adjust sign based on quadrant */
    r = (i & 2) ? (0.0f - r) : r;

    return r;
}

事实证明,在这种特殊情况下,使用 FMA 在准确性方面只提供了很小的好处。如果我将呼叫替换为fmaf(a,b,c) with ((a)*(b)+(c)),最大误差最小增加至 1.451367 ulps,即保持在 1.5 ulps 以下。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

仅使用单精度浮点近似 [0,pi] 上的余弦 的相关文章

  • 使用 mono/nunit-console/4 在 Mac OS X 控制台上运行测试

    我安装了 Max OS X 10 11 1 上面装有 Xamarin 我编写了简单的测试类 只是为了测试在 Mac OS X 和 Ubuntu 上运行 Nunit 测试 该类实际上有一个返回字符串的方法 using System names
  • 在实体框架拦截器中向 DbScanExpression 添加内部联接

    我正在尝试使用实体框架 CommandTree 拦截器通过 DbContext 向每个查询添加过滤器 为了简单起见 我有两个表 一个称为 User 有两列 UserId 和 EmailAddress 另一个称为 TenantUser 有两列
  • 在 C# 中按元素相乘数组具有意想不到的性能

    我想找到按元素相乘两个数组的最佳方法 这是更广泛项目的一部分 其中性能而不是唯一的考虑因素 我今天开始用 C Linqpad 编写一些函数 因此它还没有以任何方式进行优化 下面代码的输出如下 Environment ProcessorCou
  • 我如何理解这个 C 类型声明?

    double bar int double double double double 在查看讲座幻灯片时 我发现了留给学生的练习 用简单的英语来说 什么是类型bar在这个 C 声明中 Please帮助我解决这个问题 我什至不知道从哪里开始
  • 使用 Enumerable.OfType() 或 LINQ 查找特定类型的所有子控件

    Existed MyControl1 Controls OfType
  • 类特定的新删除运算符是否必须声明为静态

    标准中是否要求类特定的 new new delete 和 delete 是静态的 我可以让它们成为非静态成员运算符吗 为什么需要它们是静态的 它们被隐式声明为静态 即使您没有键入 static
  • 找不到 assimp-vc140-mt.dll ASSIMP

    我已经从以下位置下载了 Assimp 项目http assimp sourceforge net main downloads html http assimp sourceforge net main downloads html Ass
  • 时间:2019-03-17 标签:c#ThreadSafeDeepCopy

    我一直在阅读很多其他问题以及大量谷歌搜索 但我一直无法找到明确的解决方案 根据我读过的一些最佳实践 类的静态方法应该创建线程安全的 并且实例成员应该将线程安全留给消费者 我想为该类实现深度复制方法 该类本身还有其他引用类型成员 有没有什么方
  • 类的成员复制

    在学习 复制成员 概念时 书中给出了如下说法 此外 如果非静态成员是引用 const 或没有复制赋值的用户定义类型 则无法生成默认赋值 我不太明白这个声明到底想传达什么 或者说这个说法指的是哪一种场景 谢谢 该语句与编译器自动为您编写的类
  • vs2008 c#:Facebook.rest.api如何使用它来获取好友列表?

    如何在此基础上取得进一步的进步 获取好友列表的下一步是什么 string APIKey ConfigurationManager AppSettings API Key string APISecret ConfigurationManag
  • Visual Studio Code:如何配置 includePath 以获得更好的 IntelliSense 结果

    我是使用 Visual Studio Code 的完全初学者 我不知道我在做什么 我已经四处搜索 也许还不够 但我找不到像我这样的人如何配置的简单解释c cpp properties json每当我单击带有绿色波浪线下划线的行旁边的黄色灯泡
  • 给出 5 个参数,但在终端中只得到 3 个参数

    我想将一个文件传递给一个c 程序 如果我在 IDE 中执行此操作 test string string lt test txt return argc 5 但在终端上我刚刚得到argc 3 看来 这是因为 什么是 lt 意思是 我正在使用
  • 无法在内存位置找到异常源:cudaError_enum

    我正在尝试确定 Microsoft C 异常的来源 test fft exe 中 0x770ab9bc 处的第一次机会异常 Microsoft C 异常 内存位置 0x016cf234 处的 cudaError enum 我的构建环境是 I
  • 是否有相当于 Clang/LLVM 的 .spec 文件,在哪里可以找到参考?

    The gcc驱动程序可以配置为使用特定的链接器 特定的选项和其他细节 例如覆盖系统头 specs files 当前 截至撰写本文时 GCC 版本 4 9 0 的手册此处描述了规范文件 https gcc gnu org onlinedoc
  • 如何分析组合的 python 和 c 代码

    我有一个由多个 python 脚本组成的应用程序 其中一些脚本正在调用 C 代码 该应用程序现在的运行速度比以前慢得多 因此我想对其进行分析以查看问题所在 是否有工具 软件包或只是一种分析此类应用程序的方法 有一个工具可以将 python
  • 如何在c的case语句中使用省略号?

    CASE expr no commas ELLIPSIS expr no commas 我在c的语法规则中看到了这样的规则 但是当我尝试重现它时 int test float i switch i case 1 3 printf hi 它失
  • C# 中的 strstr() 等效项

    我有两个byte 我想找到第二个的第一次出现byte 在第一个byte 或其中的一个范围 我不想使用字符串来提高效率 翻译第一个byte to a string会效率低下 基本上我相信就是这样strstr 在 C 中做 最好的方法是什么 这
  • cout 和字符串连接

    我刚刚复习了我的 C 我尝试这样做 include
  • 每个数据库多个/单个 *.edmx 文件

    我有一个通过 ADO net 数据服务与数据库交互的项目 数据库很大 近 150 个具有依赖关系的表 该项目几年前开始 当时使用的是数据集 现在我们正在转向实体模型关系 由于我们添加了更多需要使用的表 该模型正在不断增长 这是管理这一切的正
  • 使我的 COM 程序集调用异步

    我刚刚 赢得 了在当前工作中维护用 C 编码的遗留库的特权 这个dll 公开使用 Uniface 构建的大型遗留系统的方法 除了调用 COM 对象之外别无选择 充当此遗留系统与另一个系统的 API 之间的链接 在某些情况下 使用 WinFo

随机推荐

  • 如何使用 sed 只删除三个空行?

    如何使用 sed 只删除三个空行 例如 我的文本 txt line1 line2 line3 line4 使用 sed 我希望结果看起来像这样 我的文本 txt line1 line2 line3 line4 我能够删除双空行 sed i
  • Azure SignalR 服务连接未处于活动状态

    我从 2 4 0 更新了我们的信号包并添加了RunAzureSignalR代替RunSignalR 在 de 中添加了此代码Startup cs app Map signalr map gt var hubConfiguration new
  • LISP - 如何获得嵌套列表的平均长度?

    我有个问题 我需要从此列表中获取平均长度 1 2 3 4 5 6 7 8 9 应该是2 我不知道从哪里开始 我试图得到 1 2 3 4 5 6 7 8 9 from 1 2 3 4 5 6 7 8 9 但我失败了 因为 reduce app
  • Google 是否提供可用于获取手机位置的 API?

    我的智能手机向 Google G 和 Android 设备管理器 报告我的位置 我想从网站 程序中读取该位置来绘制我的位置 我可以使用智能手机上的另一个应用程序进行额外的跟踪 但这往往会消耗相当多的电池 有两个应用程序进行跟踪 由于纬度已被
  • 由于 Windows 之前冻结,Outlook 宏被禁用

    我们公司在每台安装 Outlook 的计算机上都使用 VBA 宏 宏使用证书进行数字签名以确保安全 该证书是通过以下方式生成的自认证程序应用 当我们在 Outlook 中的 VBA 项目 包含宏 上添加数字签名时 我们选择之前生成的证书 并
  • 确定 CALayer 旋转了多少

    我有一个程序 其中 CALayer 必须旋转到特定值 如何确定 CALayer 的当前旋转 我有一个旋转图层的 UIRotationGestureRecognizer void handleGesture UIGestureRecogniz
  • 移动Android View并防止onDraw被一遍又一遍地调用

    我正在延长View 类 我所说的MyView 我添加了一些属性 这些属性基本上说明了在对象上绘制的内容 并处理它 我每隔几毫秒移动一次此类的对象 这效果很好 我在用着this layout left top right bottom 移动
  • Spark 连接速度呈指数级缓慢

    我正在尝试连接两个 Spark RDD 我有一个链接到类别的事务日志 我已将交易 RDD 格式化为以类别 id 作为键 transactions cat take 3 u 707 u 86246 u 205 u 7 u 707 u 1078
  • 为什么我的 d3 力导向图不显示边缘?

    我使用 d3 创建了一个简单的力导向图 http goo gl afHTD http goo gl afHTD 为什么图表的边缘不显示 这是我的整个 HTML 文件 当然 您也可以通过在我的链接页面上查看源代码来查看它并修改它 它基于 d3
  • 使用错误的表别名生成查询的原则

    我正在尝试做一个简单的 gt find 使用原则 规则 2 5 1 查询非常简单 this gt get order repository gt find 10 但这会生成一个复杂的查询 选择 s0 number AS number 0 s
  • SYSTEM_HANDLE_INFORMATION结构

    这个结构从何而来 我知道它是在著名的 ntdll h 中声明的 并且是未记录的 Windows API 的一部分 但不同版本的windows之间不是有差异吗 有没有办法从工作系统中转储这个结构 我在 Windbg 中尝试了 dt SYSTE
  • 如何复制内存

    说我有 unsigned char varA varB varC varA malloc 64 varB malloc 32 varC malloc 32 我怎样才能把first将 32 字节的 varA 放入 varB 中last32字节
  • 编写带有垂直标题的 HTML 表格的最常见方法? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 您编写具有垂直标题的 HTML 表格的首选方式是什么 通过垂直标题我的意思是表格有标题 th 标签位于左侧 通常 Header 1数据数据数据
  • 最大连续数

    我正在练习两个指针技术来解决最大连续数 LeetCode https leetcode com problems max consecutive ones 给定一个二进制数组 找出该数组中连续 1 的最大数量 示例1 Input 1 1 0
  • Html-Webpack-Plugin 模板:模块构建失败:SyntaxError:意外的标记

    当我尝试使用 index ejs 模板文件使用 html webpack plugin 进行构建时 会引发以下错误 即使我尝试加载为 html 文件或安装 ejs loader 仍然失败 我不确定 ejs loader 是否与 html w
  • mysql 错误 2002 (HY000): 无法通过套接字 '/var/run/mysqld/mysqld.sock' 连接到本地 MySQL 服务器 (2)

    我使用 在 Ubuntu 13 上 安装了 MYSQL sudo apt get install mysql 但跑完之后 mysql u root p 然后输入密码就会出现错误 ERROR 2002 HY000 无法通过套接字连接到本地 M
  • 在Python中初始化二维数组

    我在 python 中初始化二维数组时遇到问题 我想要一个 6x6 阵列 我做到了 arr None 6 6 但是当我这样做时 gt gt gt arr 1 2 10 gt gt gt arr None None 10 None None
  • 电子邮件中的 URL 是否已被搜索引擎索引,以便可以公开搜索?

    我在这里阅读了一些有关电子邮件客户端预取电子邮件中的 URL 的问题 对此的答案似乎是添加一个新的确认页面 用户必须在其中单击按钮来确认所需的操作 But this https stackoverflow com a 42147812 11
  • 外键到底是什么?

    好的 所以我知道数据库中的主键是什么 如果数据库中有一个表 则主键是表中每一行唯一的单个值 例如 id name whatever 1 Alice 2 Bob 45 Eve 988 所以我需要一个好的 简单的例子来解释外键到底是什么 因为我
  • 仅使用单精度浮点近似 [0,pi] 上的余弦

    我目前正在研究余弦的近似值 由于最终的目标设备是自行开发的 32 位浮点 ALU LU 并且有专门的 C 编译器 因此我无法使用 C 库数学函数 cosf 我的目标是编写在准确性和指令 周期数量方面有所不同的各种方法 我已经尝试过很多不同的