从CAMPARY输出值

时间:2019-03-14 22:05:51

标签: arbitrary-precision multiprecision

我正在尝试使用CAMPARY库(CudA多重精度算法库)。我已经下载了代码并将其包含在我的项目中。由于它同时支持cpu和gpu,因此我将从cpu开始以了解其工作原理并确保它能够满足我的需求。但目的是将它与CUDA一起使用。

我能够实例化一个实例并分配一个值,但是我不知道如何把事情弄回来。考虑:

#include <time.h>
#include "c:\\vss\\CAMPARY\\Doubles\\src_cpu\\multi_prec.h"

int main()
{
    const char *value = "123456789012345678901234567";

    multi_prec<2> a(value);

    a.prettyPrint();
    a.prettyPrintBin();
    a.prettyPrintBin_UnevalSum();
    char *cc = a.prettyPrintBF();
    printf("\n%s\n", cc);
    free(cc);
}

编译,链接,运行(VS 2017)。但是输出结果毫无帮助:

Prec = 2
   Data[0] = 1.234568e+26
   Data[1] = 7.486371e+08

Prec = 2
   Data[0] = 0x1.987bf7c563caap+86;
   Data[1] = 0x1.64fa5c3800000p+29;

0x1.987bf7c563caap+86 + 0x1.64fa5c3800000p+29;

1.234568e+26 7.486371e+08

像这样打印每个双打可能很容易,但是并不能告诉您有关所存储的128数字的值。如果无法输出结果,则执行高精度计算的价值有限。

除了仅打印出值外,最终我还需要将这些数字转换为整数(如果有打印方法,我愿意尝试使用浮点数,但我担心准确性和速度都会受到影响。 )。与MPIR(不支持CUDA)不同,CAMPARY没有任何关联的多精度int类型,只是浮点数。我可能可以拼凑所需的内容(主要是加/减/比较),但前提是我可以取回CAMPARY值的整数部分,但我看不出有办法。

CAMPARY似乎没有任何文档,所以可以想象这些功能在那里,而我只是忽略了它们。我想在CAMPARY论坛/邮件列表中提问,但似乎没有。这就是为什么我在这里问。

总结:

  1. 是否可以从CAMPARY中输出128位(multi_prec<2>)值?
  2. 有什么方法可以从CAMPARY multi_prec中提取整数部分吗?也许我不理解的库中的(许多)数学函数之一对此进行了计算?

1 个答案:

答案 0 :(得分:-1)

这个问题实际上只有两个可能的答案:

  1. 还有另一个(更好)的多精度库可在CUDA上运行,满足您的需求。
  2. 这是修改此库以执行所需功能的方法。

唯一能给出第一个答案的人就是CUDA程序员。不幸的是,如果有这样一个图书馆,我感到有足够的信心会知道并提到它。

对于#2,如果不是CUDA程序员,为什么有人会更新此库?还有其他更好的多精度库。 CAMPARY提供的唯一好处是它支持CUDA。这意味着CUDA程序员是唯一真正有意使用或修改该库的人。

而且,作为对解决这一问题最感兴趣的CUDA程序员,我确实找到了一种解决方案(尽管很丑陋)。我将其发布在这里,希望这些信息对将来的CAMPARY程序员有价值。该库没有太多信息,所以这是一个开始。


您需要了解的第一件事是CAMPARY如何存储其数据。而且,尽管并不复杂,但这并不是我所期望的。来自MPIR,我假设CAMPARY的数据存储方式几乎相同:固定大小的指数,后跟任意数量的尾数。

但是不,CAMPARY采取了不同的方式。查看代码,我们看到:

private:
    double data[prec];

现在,我认为这只是保留它们所需位数的一种任意方法。但是不,他们确实使用prec双打。像这样:

multi_prec<8> a("2633716138033644471646729489243748530829179225072491799768019505671233074369063908765111461703117249");

    // Looking at a in the VS debugger:

    [0] 2.6337161380336443e+99  const double
    [1] 1.8496577979210756e+83  const double
    [2] 1.2618399223120249e+67  const double
    [3] -3.5978270144026257e+48 const double
    [4] -1.1764513205926450e+32 const double
    [5] -2479038053160511.0 const double
    [6] 0.00000000000000000 const double
    [7] 0.00000000000000000 const double

因此,他们正在做的是在第一个double中存储最大可能的精度,然后使用余数来计算下一个double,依此类推,直到它们涵盖了整个值,或者用完精度为止(丢弃最低有效位)。请注意,其中一些是负数,这意味着先前值的总和要比实际值大一点,并且它们正在向下修正。

考虑到这一点,我们回到如何打印它的问题。

理论上,您可以将所有这些加在一起以获得正确的答案。但是根据定义,我们已经知道C没有数据类型来保存此大小的值。但是其他图书馆也有(例如MPIR)。现在,MPIR不能在CUDA上运行,但不需要。您不想让CUDA代码打印出数据。无论如何,您应该从主机执行此操作。因此,使用CUDA的全部功能进行计算,将结果cudaMemcpy返回,然后使用MPIR将其打印出来:

#define MPREC 8
void ShowP(const multi_prec<MPREC> value)
{
    multi_prec<MPREC> temp(value), temp2;

    // from mpir at mpir.org
    mpf_t mp, mp2;

    mpf_init2(mp, value.getPrec() * 64); // Make sure we reserve enough room
    mpf_init(mp2); // Only needs to hold one double.

    const double *ptr = value.getData();

    mpf_set_d(mp, ptr[0]);

    for (int x = 1; x < value.getPrec(); x++)
    {
        // MPIR doesn't have a mpf_add_d, so we need to load the value into
        // an mpf_t.
        mpf_set_d(mp2, ptr[x]);
        mpf_add(mp, mp, mp2);
    }

    // Using base 10, write the full precision (0) of mp, to stdout.
    mpf_out_str(stdout, 10, 0, mp); 

    mpf_clears(mp, mp2, NULL);
}

与上面的multi_prec中存储的数字一起使用,这将输出完全相同的值。是的。

这不是一个特别优雅的解决方案。显然必须添加第二个库以仅打印第一个库的值是次佳的。而且这种转换也不能那么快。但是,打印的频率通常比计算的频率要低得多。如果您要花费一个小时的计算时间和少量的打印,那么性能就没什么大不了的。而且,它根本无法打印。

CAMPARY有很多缺点(无限制,不受支持,无法维护)。但是对于需要CUDA上的mp编号的人(尤其是如果需要sqrt的人),这是我找到的最佳选择。