Можно ли безопасно переинтерпретировать std::vector<std::complex<boost:multiprecision::float128>>(N).data() в fftwq_complex*?

StackOverflow https://stackoverflow.com//questions/25061663

Вопрос

На самом деле я не ожидал, что следующий пример сработает, но он действительно работает (g++ 4.6.4, с --std=c++0x):

#include <boost/multiprecision/float128.hpp> 
#include <blitz/array.h>
#include <fftw3.h>


int main(int /*argc*/, char** /*argv*/)
{
    //these are the same
    std::cout << sizeof(std::complex<boost::multiprecision::float128>) << " " << sizeof(fftwq_complex) << std::endl;


    typedef std::vector< std::complex<boost::multiprecision::float128> >   boost128cvec;
    //typedef std::vector<std::complex<boost::multiprecision::float128> , fftw::allocator< std::complex<boost::multiprecision::float128> > >   boost128cvec;

    //declare a std::vector consisting of std::complex<boost::multiprecision::float128>
    boost128cvec test_vector3(12);

    //casting its data storatge to fftwq_complex*
    fftwq_complex* test_ptr3 = reinterpret_cast<fftwq_complex*>(test_vector3.data());

    //also create a view to the same data as a blitz::Array
    blitz::Array<std::complex<boost::multiprecision::float128>, 1> test_array3(test_vector3.data(), blitz::TinyVector<int, 1>(12), blitz::neverDeleteData);

    test_vector3[3] = std::complex<boost::multiprecision::float128>(1.23,4.56);

    //this line would not work with std::vector
    test_array3 = sin(test_array3);

    //this line would not work with the built-in type __float128
    test_vector3[4] = sin(test_vector3[3]);

    //all of those print the same numbers
    std::cout << "fftw::vector: " << test_vector3[3].real()       << " + i " << test_vector3[3].imag() << std::endl;
    std::cout << "fftw_complex: " << (long double)test_ptr3[3][0] << " + i " << (long double)test_ptr3[3][1] << std::endl;
    std::cout << "blitz:        " << test_array3(3).real()        << " + i " << test_array3(3).imag() << std::endl << std::endl;

}

Два замечания:

  • Цель состоит в том, чтобы иметь возможность использовать оба fftw и blitz::Array операции с одними и теми же данными без необходимости их копирования, в то же время имея возможность использовать общие функции, такие как sin() также для комплексных переменных с четырехкратной точностью
  • А blitz-part работает нормально, что и ожидалось.Но сюрпризом (для меня) было то, что fftwq_complex* часть тоже работает нормально.
  • А fftw::allocator это простая замена std::allocator который будет использовать fftwq_malloc чтобы обеспечить правильное выравнивание SIMD, но это не важно для этого вопроса, поэтому я его пропустил (по крайней мере, я думаю, что это не важно для этого вопроса)

Мой вопрос:Насколько тонкий лед, на который я ступаю?

Это было полезно?

Решение

Вы в значительной степени экономите:

  • std::vector совместим с массивом C (вы можете получить доступ к указателю на первый элемент через vector.data(), как ответили в этот вопрос
  • std::complex<T> разработан для совместимости с массивом формы T[2], который совместим с FFTW.Это описано в Документация ФФТВ

    В C++ имеется собственный сложный класс шаблона, определенный в стандартном заголовочном файле.Как сообщается, комитет по стандартизации C++ недавно согласился обязать формат хранения, используемый для этого типа, быть бинарно-совместимым с типом C99, т.е.массив T[2] с последовательными вещественными [0] и мнимыми [1] частями.(См. отчет http://www.open-std.org/jtc1/sc22/WG21/docs/papers/2002/n1388.pdf WG21/N1388.) Хотя на момент написания этой статьи предложение не являлось частью официального стандарта, в нем говорилось, что:«Это решение было протестировано во всех текущих основных реализациях стандартной библиотеки и показано, что работает». В той степени, в которой это правда, если у вас есть переменная комплекс *x, вы можете передать его непосредственно FFTW через ReinterPret_CASC (x).

Единственное, что следует иметь в виду, это то, что data() становится недействительным, если вы добавляете значения в свой вектор.


В последней части есть совместимость между boost::multiprecision::float128 и __float128.Документация по повышению не дает никаких гарантий по этому поводу.Однако можно добавить несколько статических утверждений в ваш код, что не удастся, если преобразование невозможно.Это может выглядеть так:

static_assert(std::is_standard_layout<float128>::value,"no standard type");
static_assert(sizeof(float128) == sizeof(__float128),"size mismatch");

Где sizeof гарантирует одинаковый размер типа повышения и __float128, и is_standard_layout проверяет, что:

Указатель на класс стандартной компоновки может быть преобразован (с помощью reinterpret_cast) в указатель на его первый нестатический элемент данных и наоборот.

Конечно, это дает только подсказку, если это сработает в конечном итоге, поскольку вы не можете сказать, действительно ли тип является типом. __float128, но ab boost утверждает, что их тип представляет собой тонкую обертку вокруг него, все должно быть в порядке.Если это изменения в дизайне или структуре float128, статические утверждения должны завершиться неудачно.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top