【问题标题】:How to get the double value using libpq?如何使用 libpq 获得双精度值?
【发布时间】:2013-12-10 19:59:06
【问题描述】:

libpq 文档中的 examples 展示了如何通过将整数值转换为主机端表示来获取整数值。

我很好奇必须做什么才能使用 libpq(没有 libpqtyppes)获得双精度值?我试过reinterpret_cast,但没有成功。

还有为什么文本和字节数据不需要字节序转换?

数据库在 Windows 7 上本地运行,我使用的是 Visual C++ 2013。

pptr 是我要检索的双重值。

#include <iostream>
#include <memory>
#include <vector>
#include <libpq-fe.h>
#include <Winsock2.h>


static void
show_binary_results(PGresult *res)
{
    int i, j;
    int i_fnum, n_fnum, p_fnum;

    /* Use PQfnumber to avoid assumptions about field order in result */
    i_fnum = PQfnumber(res, "id");
    n_fnum = PQfnumber(res, "name");
    p_fnum = PQfnumber(res, "price");

    for (i = 0; i < PQntuples(res); i++)
    {
        char* iptr;
        char* nptr;
        char* pptr;
        int         blen;
        int         ival;

        /* Get the field values (we ignore possibility they are null!) */
        iptr = PQgetvalue(res, i, i_fnum);
        nptr = PQgetvalue(res, i, n_fnum);
        pptr = PQgetvalue(res, i, p_fnum); /*THIS IS A VALUE I AM TRYING TO GET*/

        /*
        * The binary representation of INT4 is in network byte order, which
        * we'd better coerce to the local byte order.
        */
        ival = ntohl(*((uint32_t *) iptr));

        /*
        * The binary representation of TEXT is, well, text, and since libpq
        * was nice enough to append a zero byte to it, it'll work just fine
        * as a C string.
        *
        * The binary representation of BYTEA is a bunch of bytes, which could
        * include embedded nulls so we have to pay attention to field length.
        */
        //blen = PQgetlength(res, i, b_fnum);

        printf("tuple %d: got\n", i);
        printf(" i = (%d bytes) %d\n",
            PQgetlength(res, i, i_fnum), ival);
        printf(" t = (%d bytes) '%s'\n",
            PQgetlength(res, i, n_fnum), nptr);
        printf(" p = (%d bytes) %f\n",
            PQgetlength(res, i, p_fnum), *reinterpret_cast<double*>(pptr));

        printf("\n\n");
    }
}


int main(int argc, char* argv [])
{
    auto conn_string = "postgresql://postgres:pwd@localhost/db";
    auto conn_deleter = [](PGconn* c) { PQfinish(c); };
    auto res_deleter = [](PGresult* r) { PQclear(r); std::cout << "deleted" << std::endl; };
    std::unique_ptr<PGconn, decltype(conn_deleter)> conn(PQconnectdb(conn_string), conn_deleter);

    if (PQstatus(conn.get()) != ConnStatusType::CONNECTION_OK)
        std::cerr << "Problem" << std::endl;

    std::vector<const char *> params{ "1" };
    std::unique_ptr < PGresult, decltype(res_deleter)> res(PQexecParams(conn.get(),
        "SELECT * FROM table_with_double WHERE id = $1",
        params.size(),       /* one param */
        NULL,    /* let the backend deduce param type */
        (const char * const *)&params.front(),
        NULL,    /* don't need param lengths since text */
        NULL,    /* default to all text params */
        1), /* ask for binary results */
        res_deleter);      

    if (PQresultStatus(res.get()) != ExecStatusType::PGRES_TUPLES_OK)
        std::cout << "SELECT failed: " << PQerrorMessage(conn.get()) << std::endl;
    show_binary_results(res.get());
}

【问题讨论】:

  • 如何调用 PQgetvalue ?
  • AFAIK、textbytea 在这个级别上基本上是赞美 char* 所以不用担心字节顺序,同样,IEEE 双精度的位布局是机器独立的,所以没有也有字节序。
  • @muistooshort,这是我所期望的,但我得到的双倍值是 0。
  • @StephaneRolland,我已经发布了代码。
  • 在这里你做一个atof 来尝试转换结果值,它不是强制转换!您可以发布与您的问题相关的代码,即您尝试过的 reinterpret_cast to float 吗?

标签: c++ postgresql double endianness libpq


【解决方案1】:

显然,双列的数据以大端形式出现,必须转换为小端。与整数相同。基于来自answer 的优秀article,我使用了一个简单的函数来交换双精度值。

double double_swap(double d)
{
    union
    {
        double d;
        unsigned char bytes[8];
    } src, dest;

    src.d = d;
    dest.bytes[0] = src.bytes[7];
    dest.bytes[1] = src.bytes[6];
    dest.bytes[2] = src.bytes[5];
    dest.bytes[3] = src.bytes[4];
    dest.bytes[4] = src.bytes[3];
    dest.bytes[5] = src.bytes[2];
    dest.bytes[6] = src.bytes[1];
    dest.bytes[7] = src.bytes[0];
    return dest.d;
}

应用此函数从数据库中检索到正确的值。

printf(" p = (%d bytes) %lf\n",
            PQgetlength(res, i, p_fnum), double_swap(*((double*) pptr)));

【讨论】:

  • 请考虑使用std::cout &lt;&lt; 而不是printf。它被认为是 C++ 而不是 C。它还可以即时进行所有常见的转换。
  • @StephaneRolland,这是来自 libpq 示例的代码,这样我的目标是表明我没有做任何不寻常的事情。我同意此代码不应通过任何代码审查。
  • 由于 OP 明确提到了 MS Windows(虽然不是 8),所以有 htonll 用于交换 64 位长整数 / 盲目转换双精度的字节顺序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-07
  • 1970-01-01
  • 2017-01-19
  • 2023-03-05
相关资源
最近更新 更多