هل يمكن إعادة تفسير std::vector<std::complex<boost:multiprecision::float128>>(N).data() بأمان إلى fftwq_complex*؟
-
22-12-2019 - |
سؤال
لم أتوقع حقًا أن يعمل المثال التالي، ولكنه بالفعل يعمل (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
-الجزء يعمل بشكل جيد، وهو ما هو متوقع.لكن المفاجأة (بالنسبة لي) كانت أنfftwq_complex*
الجزء يعمل بشكل جيد أيضًا. - ال
fftw::allocator
هو بديل بسيط لstd::allocator
والتي سوف تستخدمfftwq_malloc
للتأكد من محاذاة simd الصحيحة، لكن هذا ليس مهمًا لهذا السؤال، لذلك تركته (على الأقل أعتقد أن هذا ليس مهمًا لهذا السؤال)
سؤالي هو:ما مدى رقة الجليد الذي أدوس عليه؟
المحلول
أنت إلى حد كبير حفظ:
std::vector
متوافق مع مصفوفة C (يمكنك الوصول إلى مؤشر للعنصر الأول عبرvector.data()
, ، كما أجاب في هذا السؤالstd::complex<T>
تم تصميمه ليكون متوافقًا مع مجموعة من النماذجT[2]
, ، وهو متوافق مع FFTW.هذا موصوف في وثائق 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_cast (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
, ، يجب أن تفشل التأكيدات الثابتة.