Skip to content

Commit 45563bf

Browse files
committed
float/double to string - match Postgres behaviour more closely, and fix PG11 compatibility.
Follow-up to #4 and #5.
1 parent cdc1915 commit 45563bf

File tree

3 files changed

+70
-14
lines changed

3 files changed

+70
-14
lines changed

postgres_utils.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11

22
#include "postgres_utils.hpp"
33

4+
#include <algorithm>
5+
#include <cfloat>
6+
#include <cmath>
7+
48
extern "C" {
9+
#include <pg_config.h>
510
#include <postgres.h>
11+
#include <utils/builtins.h>
12+
#if PG_VERSION_NUM >= 120000
13+
#include <common/shortest_dec.h>
14+
#include <utils/float.h>
15+
#endif
616
}
717

818
namespace postgres_protobuf {
@@ -16,5 +26,54 @@ void* palloc0_or_throw_bad_alloc(size_t size) {
1626
return p;
1727
}
1828

29+
std::string float_to_string(float x) {
30+
// Matches behaviour of `float4out`
31+
#if PG_VERSION_NUM >= 120000
32+
char buf[32];
33+
static_assert(32 >= FLOAT_SHORTEST_DECIMAL_LEN);
34+
if (extra_float_digits > 0) {
35+
float_to_shortest_decimal_buf(x, buf);
36+
} else {
37+
pg_strfromd(buf, 32, FLT_DIG + extra_float_digits, x);
38+
}
39+
return std::string(buf);
40+
#else
41+
if (std::isnan(x)) {
42+
return "NaN";
43+
}
44+
int inf = std::isinf(x);
45+
if (inf == 1) {
46+
return "Infinity";
47+
} else if (inf == -1) {
48+
return "-Infinity";
49+
} else {
50+
int digits = std::max(1, FLT_DIG + extra_float_digits);
51+
char* buf = psprintf("%.*g", digits, x);
52+
std::string s(buf);
53+
pfree(buf);
54+
return s;
55+
}
56+
#endif
57+
}
58+
59+
std::string double_to_string(double x) {
60+
// Matches behaviour of `float8out`
61+
#if PG_VERSION_NUM >= 120000
62+
char buf[32];
63+
static_assert(32 >= DOUBLE_SHORTEST_DECIMAL_LEN);
64+
if (extra_float_digits > 0) {
65+
double_to_shortest_decimal_buf(x, buf);
66+
} else {
67+
pg_strfromd(buf, 32, DBL_DIG + extra_float_digits, x);
68+
}
69+
return std::string(buf);
70+
#else
71+
char* buf = float8out_internal(x);
72+
std::string s(buf);
73+
pfree(buf);
74+
return s;
75+
#endif
76+
}
77+
1978
} // namespace postgres_utils
2079
} // namespace postgres_protobuf

postgres_utils.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ void pdelete(T* p) {
4646
pfree(p);
4747
}
4848

49+
std::string float_to_string(float x);
50+
std::string double_to_string(double x);
51+
4952
// TODO: pass memory context explicitly?
5053
template <typename T>
5154
class PostgresAllocator : public std::allocator<T> {

querying.cpp

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
#include "querying.hpp"
22

3-
#include "descriptor_db.hpp"
4-
#include "postgres_protobuf_common.hpp"
5-
63
#include <algorithm>
74
#include <cassert>
85
#include <cstdlib>
@@ -14,6 +11,10 @@
1411
#include <utility>
1512
#include <vector>
1613

14+
#include "descriptor_db.hpp"
15+
#include "postgres_protobuf_common.hpp"
16+
#include "postgres_utils.hpp"
17+
1718
// Protobuf headers must be included before any Postgres headers because
1819
// the latter pollute names like 'FATAL' used by macros in the former.
1920
#include <google/protobuf/descriptor.h>
@@ -27,7 +28,6 @@
2728
extern "C" {
2829
// Must be included before other Postgres headers
2930
#include <postgres.h>
30-
#include <common/shortest_dec.h>
3131
}
3232

3333
namespace pb = ::google::protobuf;
@@ -464,18 +464,12 @@ class PrimitiveEmitter : public Emitter {
464464
field.wire_type, ty_);
465465
switch (ty_) {
466466
case T::TYPE_DOUBLE:
467-
{
468-
char buf[DOUBLE_SHORTEST_DECIMAL_LEN];
469-
double_to_shortest_decimal_buf(WFL::DecodeDouble(field.value.as_uint64), buf);
470-
EmitStr(std::move(std::string(buf)));
471-
}
467+
EmitStr(std::move(postgres_utils::double_to_string(
468+
WFL::DecodeDouble(field.value.as_uint64))));
472469
break;
473470
case T::TYPE_FLOAT:
474-
{
475-
char buf[FLOAT_SHORTEST_DECIMAL_LEN];
476-
float_to_shortest_decimal_buf(WFL::DecodeFloat(field.value.as_uint32), buf);
477-
EmitStr(std::move(std::string(buf)));
478-
}
471+
EmitStr(std::move(postgres_utils::float_to_string(
472+
WFL::DecodeFloat(field.value.as_uint32))));
479473
break;
480474
case T::TYPE_INT64:
481475
case T::TYPE_SFIXED64:

0 commit comments

Comments
 (0)