Вывод значений из CAMPARY - PullRequest
0 голосов
/ 15 марта 2019

Я пытаюсь использовать библиотеку CAMPARY (CudA Multiple Precision ARithmetic librarY). Я скачал код и включил его в свой проект. Поскольку он поддерживает как 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. Есть ли способ вывести 128-битные (multi_prec<2>) значения из CAMPARY?
  2. Есть ли способ извлечь целочисленную часть из CAMPARY multi_prec? Возможно, одна из (многих) математических функций в библиотеке, которую я не понимаю, вычисляет это?

1 Ответ

0 голосов
/ 16 марта 2019

На самом деле есть только 2 возможных ответа на этот вопрос:

  1. Есть еще одна (лучшая) библиотека с высокой точностью, которая работает на CUDA и выполняет то, что вам нужно.
  2. Вот какизменить эту библиотеку, чтобы она делала то, что вам нужно.

Единственные, кто может дать первый ответ, - это программисты CUDA.К сожалению, если бы была такая библиотека, я уверен, что talonmies знал бы об этом и упомянул об этом.

Что касается # 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

Итак, они сохраняют максимально возможную точность в первом дубле, затем остаток используется для вычисления следующего дубля и так далее, пока они не охватывают все значениеили не хватает точности (отбрасывая младшие значащие биты).Обратите внимание, что некоторые из них являются отрицательными, что означает, что сумма предыдущих значений немного больше, чем фактическое значение, и они корректируют его вниз.

Имея это в виду, мы возвращаемся к вопросу о том, какраспечатайте его.

Теоретически вы можете просто сложить все это вместе, чтобы получить правильный ответ.Но, по определению, мы уже знаем, что в 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 выше, это выдает точно такое же значение.Yay.

Это не особенно элегантное решение.Необходимость добавить вторую библиотеку только для того, чтобы напечатать значение из первой, явно неоптимальна.И это преобразование не может быть слишком быстрым.Но печать обычно выполняется (намного) реже, чем компьютерная.Если вы потратите на компьютерных часов и несколько отпечатков, производительность не имеет большого значения.И это намного лучше, чем вообще невозможность печатать.

У CAMPARY много недостатков (без поддержки, без поддержки, без поддержки).Но для людей, которым нужны числа mp в CUDA (особенно если вам нужен sqrt), это лучший вариант, который я нашел.

...