繁体   English   中英

将范围分成均匀间隔

[英]Split range into uniform intervals

我想将具有double边框的范围分割为N>=2等于或接近相等的间隔。

我在GNU Scientific Library中找到了一个合适的函数:

make_uniform (double range[], size_t n, double xmin, double xmax)
{
  size_t i;

  for (i = 0; i <= n; i++)
    {
      double f1 = ((double) (n-i) / (double) n);
      double f2 = ((double) i / (double) n);
      range[i] = f1 * xmin +  f2 * xmax;
    }
}

但是,什么时候
xmin = 241141 (二进制0x410D6FA800000000
xmax = 241141.0000000001 (二进制0x410D6FA800000003
N = 3
功能产生

[0x410D6FA800000000,
 0x410D6FA800000000,
 0x410D6FA800000002,
 0x410D6FA800000003]

而不是期望

[0x410D6FA800000000,
 0x410D6FA800000001,
 0x410D6FA800000002,
 0x410D6FA800000003]

如何在不使用长算术的情况下实现均匀性(我已经有一个很长的算术解决方案,但它很丑陋而且很慢)? bit twiddling和x86(x86-64,因此没有扩展精度)汇编程序例程是可以接受的。

更新:

需要一般的解决方案,没有前提是xminxmax具有相等的指数和符号:

  • xminxmax可以是除无穷大和NaN之外的任何值(为简单起见,可能还排除非规范化值)。
  • xmin < xmax
  • (1<<11)-1>=N>=2
  • 我准备好主要(2-3个订单)的性能损失

我看到两个选择:将操作重新排序为xmin + (i * (xmax - xmin)) / n ,或直接处理二进制表示。 这是两个例子。

#include <iostream>
#include <iomanip>

int main() {
    double xmin = 241141;
    double xmax = 241141.0000000001;
    size_t n = 3, i;
    double range[4];

    std::cout << std::setprecision(std::numeric_limits<double>::digits10) << std::fixed;

    for (i = 0; i <= n; i++) {
        range[i] = xmin + (i * (xmax - xmin)) / n;

        std::cout << range[i] << "\n";
    }
    std::cout << "\n";

    auto uxmin = reinterpret_cast<unsigned long long&>(xmin);
    auto uxmax = reinterpret_cast<unsigned long long&>(xmax);

    for (i = 0; i <= n; i++) {
        auto rangei = ((n-i) * uxmin + i * uxmax) / n;
        range[i] = reinterpret_cast<double&>(rangei);

        std::cout << range[i] << "\n";
    }
}

住在Coliru

x87仍然存在于x86-64中,主流操作系统的64位内核可以正确保存/恢复64位进程的x87状态。 尽管您可能已阅读过,但x87完全可用于64位代码。

在Windows之外(即x86-64 System V ABI在其他地方使用), long double是80位原生x87原生格式。 如果您不关心ARM / PowerPC的可移植性/在HW中只有64位精度的其他任何东西,这可能只解决x86 / x86-64的精度问题。

可能最好只使用long double的功能内的临时工具。

我不确定你要在Windows上做什么来让编译器发出80位扩展FP数学。 它在asm中肯定是可能的,并且由内核支持,但是工具链和ABI使得使用起来不方便。


x87仅比当前CPU上的标量SSE数学慢一些。 但是,80位加载/存储速度非常慢,例如Skylake上的4 uops而不是1( https://agner.org/optimize/ ),以及fld m80的几个周期额外延迟。

对于你的循环必须通过存储和使用x87 fild将int转换为FP,它可能最多比一个好的编译器可以用SSE2为64位double的速度慢2倍。

当然, long double会阻止自动矢量化。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM