std::vector<std::complex<boost:multiprecision::float128>>(N).data() pode ser reinterpret_casted com segurança para fftwq_complex*?

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

Pergunta

Eu realmente não esperava que o exemplo a seguir funcionasse, mas realmente funciona (g++ 4.6.4, com --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;

}

Duas observações:

  • O objetivo é poder usar ambos fftw e blitz::Array operações nos mesmos dados sem a necessidade de copiá-los e, ao mesmo tempo, poder usar funções genéricas como sin() também para variáveis ​​complexas com precisão quádrupla
  • O blitz-part funciona bem, o que é esperado.Mas a surpresa (para mim) foi que o fftwq_complex* parte também funciona bem.
  • O fftw::allocator é um simples substituto para std::allocator que usará fftwq_malloc para garantir o alinhamento correto do simd, mas isso não é importante para esta questão, então deixei de fora (pelo menos acho que isso não é importante para esta questão)

Minha pergunta é:Quão fino é o gelo em que estou pisando?

Foi útil?

Solução

Você está praticamente salvo:

  • std::vector é compatível com um array C (você pode acessar um ponteiro para o primeiro elemento via vector.data(), conforme respondido em essa questão
  • std::complex<T> foi projetado para ser compatível com um array de formato T[2], que é compatível com FFTW.Isto está descrito no Documentação FFTW

    C++ possui sua própria classe de modelo complexa, definida no arquivo de cabeçalho padrão.Alegadamente, o comitê de padrões C++ concordou recentemente em exigir que o formato de armazenamento usado para este tipo seja compatível com binário com o tipo C99, ou seja,uma matriz T[2] com partes reais [0] e imaginárias [1] consecutivas.(Ver relatório http://www.open-std.org/jtc1/sc22/WG21/docs/papers/2002/n1388.pdf WG21/N1388.) Embora não fizesse parte do padrão oficial no momento da redação deste documento, a proposta afirmava que:"Esta solução foi testada com todas as principais implementações atuais da biblioteca padrão e demonstrada estar funcionando". Na medida em que isso é verdade, se você tiver um complexo variável *x, poderá passá -lo diretamente para FFTW via reinterpret_cast (x).

A única coisa a ter em mente é que o data() é invalidado se você adicionar valores ao seu vetor.


Para a última parte há a compatibilidade entre boost::multiprecision::float128 e __float128.A documentação do boost não oferece nenhuma garantia sobre isso.O que pode ser feito, entretanto, é adicionar algumas afirmações estáticas em seu código, que falharão se a conversão não for possível.Isso poderia ser assim:

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

Onde sizeof garante o mesmo tamanho do tipo boost e __float128, e is_standard_layout verifica se:

Um ponteiro para uma classe de layout padrão pode ser convertido (com reinterpret_cast) em um ponteiro para seu primeiro membro de dados não estático e vice-versa.

Claro, isso só dá dicas se funcionar no final, já que você não pode dizer se o tipo é realmente um __float128, mas ab boost afirma que seu tipo é um invólucro fino em torno dele, deve ficar bem.Se houver alterações no design ou na estrutura do float128, as asserções estáticas deverão falhar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top