将范围分为均匀间隔

时间:2018-10-21 15:46:48

标签: c++ x86 floating-point precision

我想将边界为#include <stdio.h> #include <string.h> int main (void) { char word[20]; do { scanf("%c", word); } while (strlen (word) > 0 && strlen (word) <20); printf("the word is %s", word); } 的范围分成相等或接近相等的间隔double

我在GNU Scientific Library中找到了合适的功能:

N>=2

但是,什么时候
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]

如何在不求助于长算术的情况下实现均匀性(我已经有了长算术解决方案,但它丑陋且缓慢)?位扭曲和x86(x86-64,因此没有扩展精度)汇编程序是可以接受的。

更新:

需要通用的解决方案,而不以[0x410D6FA800000000, 0x410D6FA800000001, 0x410D6FA800000002, 0x410D6FA800000003] xmin具有相等的指数和符号为前提:

  • xmaxxmin可以是除无穷大和NaN之外的任何值(为简单起见,也可能不包括非规格化值)。
  • xmax
  • xmin < xmax
  • 我已准备好在2-3个订单中出现严重的性能损失

2 个答案:

答案 0 :(得分:0)

我看到两个选择:将操作重新排序为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";
    }
}

Live on Coliru

答案 1 :(得分:0)

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

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

最好仅将long double用于函数内部的临时对象。

我不确定要使编译器发出80位扩展FP数学,您必须在Windows上做什么。在asm中当然是有可能的,并且受内核支持,但是工具链和ABI使其使用起来很不方便。


在当前CPU上,

x87仅比标量SSE数学要慢一些。不过,80位加载/存储的速度特别慢,就像在Skylake上4而不是1(https://agner.org/optimize/一样,并且fld m80还要有几个周期的额外延迟。

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

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