对称 Lerp 和编译器优化

2023-12-22

我有一个功能:

float lerp(float alpha, float x0, float x1) {
    return (1.0f - alpha) * x0 + alpha * x1;
}

对于那些还没有看过的人来说,这比x0 + (x1-x0) * alpha因为后者并不能保证lerp(1.0f, x0, x1) == x1.

现在,我想要我的lerp函数具有附加属性:我想要lerp(alpha, x0, x1) == lerp(1-alpha, x1, x0)。 (至于为什么:这是一个更复杂函数的玩具示例。)我想出的似乎有效的解决方案是

float lerp_symmetric(float alpha, float x0, float x1) {
    float w0 = 1.0f - alpha;
    float w1 = 1.0f - w0;
    return w0 * x0 + w1 * x1;
}

这种双重减法具有舍入接近零和接近一的效果,因此如果alpha = std::nextafter(0)(1.4012985e-45),那么1 - alpha == 1 and so 1 - (1-alpha) == 0。据我所知,这始终是事实1.0f - x == 1.0f - (1.0f - (1.0f - x))。似乎也有这样的效果w0 + w1 == 1.0f.

问题:

  1. 这是一个合理的做法吗?
  2. 我可以相信我的编译器会做我想做的事吗?特别是,我知道在 Windows 上它有时对部分结果使用更高的精度,并且我知道编译器可以执行一些代数运算;显然代数上 1-(1-x)==x 。

这是使用 Clang、VisualStudio 和 gcc 在 C++11 中实现的。


如果始终使用 IEEE-754 二进制浮点的一种格式(例如,基本 32 位二进制,C++ 常用的格式)float),将所有 C++ 运算符以直接且简单的方式映射到 IEEE-754 运算,则lerp_symmetric(alpha, x0, x1)(以下简称A) 等于lerp_symmetric(1-alpha, x1, x0) (B)

Proof:

  • If alpha,我们假设在 [0, 1] 中,大于或等于 1/2,则1-alpha由 Sterbenz 引理所精确。 (“精确”是指计算出的浮点结果等于数学结果;不存在舍入误差。)然后,在计算中A, w0是准确的,因为它是1-alpha, and w1是精确的,因为它的数学结果是alpha,所以它是完全可以表示的。并且,在计算领域B, w0是精确的,因为它的数学结果是alpha, and w1是准确的,因为它又是1-alpha.
  • If alpha小于 1/2,那么1-alpha可能有一些舍入误差。设结果为beta。然后,在A, w0 is beta。现在 ½ ≤beta,所以斯特本茨引理适用于评估w1 = 1.0f - w0, so w1是精确的(并且等于数学结果1-beta)。并且,在B, w0是精确的,同样由 Sterbenz 引理得出,并且等于w1 of A, and w1 (of B) 是精确的,因为它的数学结果是beta,这完全可以表示。

现在我们可以看到w0 in A equals w1 in B and w1 in A equals w0 in B。出租beta be 1-alpha在上述任何一种情况下,A and B因此返回(1-beta)*x0 + beta*x1 and beta*x1 + (1-beta)*x0, 分别。 IEEE-754 加法是可交换的(NaN 有效负载除外),因此A and B返回相同的结果。

回答问题:

  1. 我想说这是一个合理的做法。如果没有进一步思考,我不会断言没有可以改进的地方。

  2. 不,你不能相信你的编译器:

  • C++ 允许实现在评估浮点运算时使用超额精度。因此w0*x0 + w1*x1可以使用评估double, long double,或另一个精度,即使所有操作数都是float.
  • C++ 允许收缩,除非禁用,所以w0*x0 + w1*x1可以评估为fmaf(w0, x0, w1*x1),因此对其中一个乘法使用精确算术,但对另一个乘法不使用精确算术。

您可以使用以下方法部分解决此问题:

float w0 = 1.0f - alpha;
float w1 = 1.0f - w0;
float t0 = w0*x0;
float t1 = w1*x1;
return t0+t1;

C++ 标准要求在赋值和转换中丢弃多余的精度。这扩展到函数返回。 (我从内存中报告了这个和其他 C++ 规范;应该检查标准。)因此上面的每一个都会将其结果舍入为float即使最初使用了额外的精度。这将防止收缩。

(人们还应该能够通过包括来禁用收缩<cmath>并插入预处理器指令#pragma STDC FP_CONTRACT OFF。有些编译器可能不支持。)

One problem with the workaround above is that values are first rounded to the evaluation precision and then rounded to float. There are mathematical values for which, for such a value x, rounding x first to double (or another precision) and then to float produces a different result than rounding x directly to float. The dissertation A Rigorous Framework for Fully Supporting the IEEE Standard for Floating-Point Arithmetic in High-Level Programming Languages by Samuel A. Figueroa del Cid establishes that evaluating a single operation of multiplication or addition in IEEE-754 basic 64-bit floating-point (commonly used for double) and then rounding to the 32-bit format never has a double-rounding error (because these operations, given inputs that are elements of the 32-bit format, can never produce one of the troublesome x values described above).1

如果我对内存中报告的 C++ 规范的看法是正确的,那么只要 C++ 实现使用标称格式或足够宽的格式来计算浮点表达式以满足 Figueroa del Cid 给出的要求,上述解决方法就应该完成。

Footnote

1 Per Figueroa del Cid, if x and y have p-bit significands, and x+y or x*y is computed exactly and then rounded to q places, a second rounding to p places will have the same answer as if the result were directly rounded to p places if p ≤ (q1)/2. This is satisfied for IEEE-754 basic 32-bit binary floating-point (p = 24) and 64-bit (q = 53). These formats are commonly used for float and double, and the workaround above should suffice in a C++ implementation that uses them. If a C++ implementation evaluated float using a precision that did not satisfy the condition Figueroa del Cid gives, then double-rounding errors could occur.

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

对称 Lerp 和编译器优化 的相关文章

  • 无法使用已与其底层 RCW 分离的 COM 对象。在 oledb 中

    我收到此错误 但我不知道我做错了什么 下面的代码在backrgroundworker中 将异常详细信息复制到剪贴板 System Runtime InteropServices InvalidComObjectException 未处理 通
  • 是否可以强制 XMLWriter 将元素写入单引号中?

    这是我的代码 var ptFirstName tboxFirstName Text writer WriteAttributeString first ptFirstName 请注意 即使我使用 ptFirstName 也会以双引号结束 p
  • 如何使用GDB修改内存内容?

    我知道我们可以使用几个命令来访问和读取内存 例如 print p x 但是如何更改任何特定位置的内存内容 在 GDB 中调试时 最简单的是设置程序变量 参见GDB 分配 http sourceware org gdb current onl
  • 如何在列表框项目之间画一条线

    我希望能够用水平线分隔列表框中的每个项目 这只是我用于绘制项目的一些代码 private void symptomsList DrawItem object sender System Windows Forms DrawItemEvent
  • 如何忽略“有符号和无符号整数表达式之间的比较”?

    谁能告诉我必须使用哪个标志才能使 gcc 忽略 有符号和无符号整数表达式之间的比较 警告消息 gcc Wno sign compare 但你确实应该修复它警告你的比较
  • 实时服务器上的 woff 字体 MIME 类型错误

    我有一个 asp net MVC 4 网站 我在其中使用 woff 字体 在 VS IIS 上运行时一切正常 然而 当我将 pate 上传到 1and1 托管 实时服务器 时 我得到以下信息 网络错误 404 未找到 http www co
  • WPF TabControl,用C#代码更改TabItem的背景颜色

    嗨 我认为这是一个初学者的问题 我搜索了所有相关问题 但所有这些都由 xaml 回答 但是 我需要的是后台代码 我有一个 TabControl 我需要设置其项目的背景颜色 我需要在选择 取消选择和悬停时为项目设置不同的颜色 非常感谢你的帮助
  • 指针减法混乱

    当我们从另一个指针中减去一个指针时 差值不等于它们相距多少字节 而是等于它们相距多少个整数 如果指向整数 为什么这样 这个想法是你指向内存块 06 07 08 09 10 11 mem 18 24 17 53 7 14 data 如果你有i
  • 如何衡量两个字符串之间的相似度? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 给定两个字符串text1 and text2 public SOMEUSABLERETURNTYPE Compare string t
  • 从库中捕获主线程 SynchronizationContext 或 Dispatcher

    我有一个 C 库 希望能够将工作发送 发布到 主 ui 线程 如果存在 该库可供以下人员使用 一个winforms应用程序 本机应用程序 带 UI 控制台应用程序 没有 UI 在库中 我想在初始化期间捕获一些东西 Synchronizati
  • 插入记录后如何从SQL Server获取Identity值

    我在数据库中添加一条记录identity价值 我想在插入后获取身份值 我不想通过存储过程来做到这一点 这是我的代码 SQLString INSERT INTO myTable SQLString Cal1 Cal2 Cal3 Cal4 SQ
  • 需要哪个版本的 Visual C++ 运行时库?

    microsoft 的最新 vcredist 2010 版 是否包含以前的版本 2008 SP1 和 2005 SP1 还是我需要安装全部 3 个版本 谢谢 你需要所有这些
  • 将文本叠加在图像背景上并转换为 PDF

    使用 NET 我想以编程方式创建一个 PDF 它仅包含一个背景图像 其上有两个具有不同字体和位置的标签 我已阅读过有关现有 PDF 库的信息 但不知道 如果适用 哪一个对于如此简单的任务来说最简单 有人愿意指导我吗 P D 我不想使用生成的
  • 32 位到 64 位内联汇编移植

    我有一段 C 代码 在 GNU Linux 环境下用 g 编译 它加载一个函数指针 它如何执行并不重要 使用一些内联汇编将一些参数推送到堆栈上 然后调用该函数 代码如下 unsigned long stack 1 23 33 43 save
  • 为什么 C# Math.Ceiling 向下舍入?

    我今天过得很艰难 但有些事情不太对劲 在我的 C 代码中 我有这样的内容 Math Ceiling decimal this TotalRecordCount this PageSize Where int TotalRecordCount
  • Process.Start 阻塞

    我正在调用 Process Start 但它会阻止当前线程 pInfo new ProcessStartInfo C Windows notepad exe Start process mProcess new Process mProce
  • x86 上未对齐的指针

    有人可以提供一个示例 将指针从一种类型转换为另一种类型由于未对齐而失败吗 在评论中这个答案 https stackoverflow com questions 544928 reading integer size bytes from a
  • 防止索引超出范围错误

    我想编写对某些条件的检查 而不必使用 try catch 并且我想避免出现 Index Out of Range 错误的可能性 if array Element 0 Object Length gt 0 array Element 1 Ob
  • 使用按位运算符相乘

    我想知道如何使用按位运算符将一系列二进制位相乘 但是 我有兴趣这样做来查找二进制值的十进制小数值 这是我正在尝试做的一个例子 假设 1010010 我想使用每个单独的位 以便将其计算为 1 2 1 0 2 2 1 2 3 0 2 4 虽然我
  • 恢复上传文件控制

    我确实阅读了以下帖子 C 暂停 恢复上传 https stackoverflow com questions 1048330 pause resume upload in c 使用 HTTP 恢复上传 https stackoverflow

随机推荐