我想在 -pi/4 到 pi/4 范围内快速将 tan(x) 近似到 1 ULP 以内。我已经找到了一个几乎足够好的解决方案,但即使有相当多的计算时间和模拟退火,最后一个因子仍然让我无法理解。
我寻求 tan(x) 的特定数值近似,其形式为:
tan(x) = x*P(x^2)/Q(x^2)
其中 P 和 Q 都是 x^2 的三次方。我需要对近似值施加一些额外的约束。即匹配零时的函数值和梯度,理想情况下也是 pi/4 时的函数值和梯度。我暂时决定放宽 pi/4 的梯度约束。我应该在这里说,我所寻求的近似值在哈特的计算机近似值“圣经”中不幸缺失,这表明收敛是一个相当棘手的过程。
通常他会给出整个序列,但 tan 和 pi/4 的表会跳过 3,3,这很奇怪,因为它对于 64 位实数来说应该是完美的。精度是P、Q中N、M分别为最高非零系数所得到的正确小数位数
| 精确 | 氮 | 中号 | 指数 | 
|---|---|---|---|
| 10.66 | 2 | 2 | 4283 | 
| 13.62 | 2 | 3 | 4284 | 
| 19.74 | 4 | 3 | 4285 | 
| 19.94 | 3 | 4 | 4286 | 
我得到的系数是通过模拟退火和一些本地狡猾的方法从 Pade 近似中推导出来的,这些方法扰乱了局部最小值的解决方案。还可以,但对于我的目的来说仍然不够好。有一个顽固的补丁~0.14,其中相对误差峰值保持在 5.5e-16(大约十亿分之 60 测试用例),并且 4.4e-16 箱中的值也稍微太多。这是我迄今为止在 10^9 随机挑战测试中得到的相对误差直方图:
-5.5511e-16 | 0
-4.4409e-16 | 704272
-3.3307e-16 | 0
-2.2204e-16 | 157067630
-1.1102e-16 | 0 …我一直在查看一些代码,这些代码有一个奇怪的优化错误,在这个过程中,偶然发现了 中的错误条件strtod(),它与 中的行为不同strtof(),就在非正规值的边缘。的行为strtof()对我来说似乎完全合理,但事实strtod()并非如此!具体来说,它返回-0.0输入值"-0x1.fffffffffffffp-1023"。
"-0x1.ffffffffffffep-1023"这是在正确解码的表示中设置为 1 的一个额外位。更奇怪的是,添加额外的尾随数字会得到一个值2 -1018,我无法解释。在我看来,从非正规浮点数过渡到正常浮点数的特殊边缘情况处理不正确,导致值为零。
谁能解释额外的额外数字引起的另一个奇怪的数字?
MSC 2022 和 Intel 2023 上的故障相同
用于双打和输出的示例代码 MRE(浮点数按您的预期工作)
// strtod() fails to handle edge case overflow from denormals correctly
// 
// Problem manifests on both MS 2022 and Intel 2023 compilers so by design? but why???
// 
// using Windows 11 and Microsoft Visual Studio Community 2022 (64-bit) - Version 17.1.0
 
#include <math.h>
#include <stdio.h>
#include <stdlib.h> …我在从 Intel 2023 和 MSC Visual C++ 2022 移植工作数字代码时遇到一个奇怪的问题。使用 GCC 编译的代码非常准确(太准确),因为某些库调用以完整的 80 位浮点精度工作 - 特别是 sqrt、sin 和 cos。我可以通过使用 TUI 跟踪 gdb 的库调用来反汇编库代码执行来验证这一点。
它也出现在基准计时中,因为 x87 atan2、cos、exp 和 sin 都约为 100 个周期,而 sqrt 约为 80 个周期。SSE/AVX2 代码的相应时序低于 50 个周期,大部分在 20-30 个周期左右。
奇怪的是 tan、atan是使用 AVX2 编译的。但 cos、sin、sqrt 和 atan2 在 GCC 系统库中使用旧版 x87 代码。我已经在 32 位端口和 64 位版本上尝试过此操作,并且都遇到了相同的问题。我是海湾合作委员会的新手,所以我可能忽略了一些事情。我在 Windows 上使用默认的 MinGW 端口版本 13.1.0 (MinGW-W64 i686-ucrt-posix-dwarf),它可能有其自身的特点。
顺便说一句,我刚刚注意到 MSC 2022 有时会编码 x87 sqrt,即使启用了所有 gofaster 优化和 AVX2 代码,因为这也是我之前没有注意到的基准计时中的异常值。Intel 将其编译为本机 sqrtsd,因此速度要快得多。我回到 MSC x86 进行内联汇编,以确认 x87 …