
Postgres2015全国用户大会将于11月20至21日在北京丽亭华苑酒店召开。本次大会嘉宾阵容强大,国内顶级PostgreSQL数据库专家将悉数到场,并特邀欧洲、俄罗斯、日本、美国等国家和地区的数据库方面专家助阵:
- Postgres-XC项目的发起人铃木市一(SUZUKI Koichi)
- Postgres-XL的项目发起人Mason Sharp
- pgpool的作者石井达夫(Tatsuo Ishii)
- PG-Strom的作者海外浩平(Kaigai Kohei)
- Greenplum研发总监姚延栋
- 周正中(德哥), PostgreSQL中国用户会创始人之一
- 汪洋,平安科技数据库技术部经理
- ……
|
![]() |
使用单精或双精类型时, 我们查询出来的值可能与真实存储的值有一定差别.
这里体现了眼见不为实的特征.
以下是float4, float8的输出函数.
src/backend/utils/adt/float.c
/** float4out - converts a float4 number to a string* using a standard output format*/Datumfloat4out(PG_FUNCTION_ARGS){float4 num = PG_GETARG_FLOAT4(0);char *ascii = (char *) palloc(MAXFLOATWIDTH + 1);
if (isnan(num))PG_RETURN_CSTRING(strcpy(ascii, "NaN"));
switch (is_infinite(num)){case 1:strcpy(ascii, "Infinity");break;case -1:strcpy(ascii, "-Infinity");break;default:{int ndig = FLT_DIG + extra_float_digits;
if (ndig < 1)ndig = 1;
snprintf(ascii, MAXFLOATWIDTH + 1, "%.*g", ndig, num);}}
PG_RETURN_CSTRING(ascii);}
/** float8out - converts float8 number to a string* using a standard output format*/Datumfloat8out(PG_FUNCTION_ARGS){float8 num = PG_GETARG_FLOAT8(0);char *ascii = (char *) palloc(MAXDOUBLEWIDTH + 1);
if (isnan(num))PG_RETURN_CSTRING(strcpy(ascii, "NaN"));
switch (is_infinite(num)){case 1:strcpy(ascii, "Infinity");break;case -1:strcpy(ascii, "-Infinity");break;default:{int ndig = DBL_DIG + extra_float_digits;
if (ndig < 1)ndig = 1;
snprintf(ascii, MAXDOUBLEWIDTH + 1, "%.*g", ndig, num);}}
PG_RETURN_CSTRING(ascii);}
以下是numeric的输出函数
src/backend/utils/adt/numeric.c
/** numeric_out() -** Output function for numeric data type*/Datumnumeric_out(PG_FUNCTION_ARGS){Numeric num = PG_GETARG_NUMERIC(0);NumericVar x;char *str;
/** Handle NaN*/if (NUMERIC_IS_NAN(num))PG_RETURN_CSTRING(pstrdup("NaN"));
/** Get the number in the variable format.*/init_var_from_num(num, &x);
str = get_str_from_var(&x);
PG_RETURN_CSTRING(str);}
下面来做一个简单的测试 :
postgres=# create table t3(c1 float, c2 numeric);CREATE TABLEpostgres=# \d t3Table "public.t3"Column | Type | Modifiers--------+------------------+-----------c1 | double precision |c2 | numeric |
postgres=# insert into t3 values (1.55555555555555555555555555555555555, 1.55555555555555555555555555555555555);INSERT 0 1postgres=# select * from t3;c1 | c2------------------+---------------------------------------1.55555555555556 | 1.55555555555555555555555555555555555(1 row)
从以上结果看, 我们很容易被误导, 以为c1存储的是
1.55555555555556,
其实c1存储的值并不是
1.55555555555556, 而是通过snprintf 打印的失真后的字符串.
所以这个查询是没有结果的 :
postgres=# select * from t3 where c1>=1.55555555555556;c1 | c2----+----(0 rows)
怎样让他有结果呢?
必须把输出的字符在转成numeric, 就有结果了 :
转成numeric后, 就是真的
1.55555555555556了.
postgres=# select * from t3 where c1::numeric>=1.55555555555556;c1 | c2------------------+---------------------------------------1.55555555555556 | 1.55555555555555555555555555555555555(1 row)
其实explain 的输出也采用了float8out, 看以下SQL, Filter 里面用到了转换.
postgres=# explain select * from t3 where c1>=1.5555555555555555555555555555555555555555555555555555555555555555555555555555;QUERY PLAN-------------------------------------------------------Seq Scan on t3 (cost=0.00..68.38 rows=1557 width=40)Filter: (c1 >= 1.55555555555556::double precision)(2 rows)
对于精度要求比较高的场景建议使用numeric来存储, 以免出现以上问题.