C++ Boost多精度与Python MPMIT的互操作性

2024-09-30 01:19:17 发布

您现在位置:Python中文网/ 问答频道 /正文

我有过使用boostmultiprecision和Python的mpmath的经验。你知道吗

当它开始进行二者通信(例如,在C++中创建Python扩展)时,我的尝试总是涉及到某种浪费的浮点到字符串和字符串到浮点转换。你知道吗

我的问题是:有没有可能使双方的沟通更有效(和优雅)的方式?我的意思是,有没有一种方法直接将C++的Boost多精度负载从同一个URL中导出到Python ^ {< CD1> }对象{{a1}?你知道吗

我已经找了很久了。only other similar question I found只是使用pybind11将Boost Multiprecision导出到Python(通常),而不是直接导出到mpmath对象。在这个问题上,OP使用了我试图避免的相同方法(即,从/C++到Python通信时,从字符串转换到字符串)。你知道吗


Tags: 对象方法字符串urla1方式浪费精度
1条回答
网友
1楼 · 发布于 2024-09-30 01:19:17

这只部分回答了你的问题。因为直接的答案是:不,如果没有对字符串的浪费性转换,就不可能以干净的方式进行转换,因为mpmath是一个纯粹的python库,没有任何部分是用CC++编写的,因此即使您试图通过寻求某种二进制兼容性来跳过“浪费性转换”,您的代码也将非常脆弱:它是每当一些pythonmpmath内部结构发生微小变化时,都会中断。你知道吗

然而,我需要完全相同的东西。所以我决定通过boost::python注册一个自动转换,它使用字符串进行检查和转换。实际上,在python中,您还可以从字符串创建mpmath.mpf对象,因此基本相同,只是在下面的代码中,它的速度更快,因为它是在C++中编写的。你知道吗

以下是对我有效的方法:

#include <boost/python.hpp>
#include <iostream>
#include <limits>
#include <sstream>
#include <boost/math/constants/constants.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>
namespace py = ::boost::python;
using Prec80 = boost::multiprecision::number<boost::multiprecision::cpp_bin_float<80>>;

template<typename ArbitraryReal>
struct ArbitraryReal_to_python {
    static PyObject* convert(const ArbitraryReal& val){
        std::stringstream ss{};
        ss << std::setprecision(std::numeric_limits<ArbitraryReal>::digits10+1) << val;
        py::object mpmath = py::import("mpmath");
        mpmath.attr("mp").attr("dps")=int(std::numeric_limits<ArbitraryReal>::digits10+1);
        py::object result = mpmath.attr("mpf")(ss.str());
        return boost::python::incref( result.ptr() );
    }
};

template<typename ArbitraryReal>
struct ArbitraryReal_from_python {
    ArbitraryReal_from_python(){
         boost::python::converter::registry::push_back(&convertible,&construct,boost::python::type_id<ArbitraryReal>());
    }
    static void* convertible(PyObject* obj_ptr){
        // Accept whatever python is able to convert into float
        // This works with mpmath numbers. However if you want to accept strings as numbers this checking code can be a little longer to verify if string is a valid number.
        double check = PyFloat_AsDouble(obj_ptr);
        return (PyErr_Occurred()==nullptr) ? obj_ptr : nullptr;
    }
    static void construct(PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data){
        std::istringstream ss{ py::call_method<std::string>(obj_ptr, "__str__") };
        void* storage=((boost::python::converter::rvalue_from_python_storage<ArbitraryReal>*)(data))->storage.bytes;
        new (storage) ArbitraryReal;
        ArbitraryReal* val=(ArbitraryReal*)storage;
        ss >> *val;
        data->convertible=storage;
    }
};


struct Var
{
    Prec80 value{"-71.23"};
    Prec80 get() const   { return value; };
    void set(Prec80 val) { value = val;  };
};

BOOST_PYTHON_MODULE(pysmall)
{
    ArbitraryReal_from_python<Prec80>();
    py::to_python_converter<Prec80,ArbitraryReal_to_python<Prec80>>();

    py::class_<Var>("Var" )
        .add_property("val", &Var::get, &Var::set);
}

现在使用以下命令编译此代码:

g++ -O1 -g pysmall.cpp -o pysmall.so -std=gnu++17 -fPIC -shared -I/usr/include/python3.7m/ -lboost_python37 -lpython3.7m -Wl,-soname,"pysmall.so"

下面是一个python会话示例:

In [1]: import pysmall
In [2]: a=pysmall.Var()
In [3]: a.val
Out[3]: mpf('-71.2299999999999999999999999999999999999999999999999999999999999999999999999999997072')
In [4]: a.val=123.12
In [5]: a.val
Out[5]: mpf('123.120000000000000000000000000000000000000000000000000000000000000000000000000000003')

C++代码并不关心mpmath是否已经在python中导入。如果是,则获取现有的库句柄,如果不是,则导入它。 如果你发现这个片段有任何改进的地方,请告诉我!你知道吗

以下是我写这篇文章时的一些有用的参考资料:

  1. https://misspent.wordpress.com/2009/09/27/how-to-write-boost-python-converters/
  2. https://github.com/bluescarni/mppp/blob/master/include/mp%2B%2B/extra/pybind11.hpp(但我不想使用pybind11,只是boost::python

相关问题 更多 >

    热门问题