我正在尝试使用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论坛/邮件列表中提问,但似乎没有。这就是为什么我在这里问。
总结:
multi_prec<2>
)值?答案 0 :(得分:-1)
这个问题实际上只有两个可能的答案:
唯一能给出第一个答案的人就是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的人),这是我找到的最佳选择。