don't mistake the float/double's display & real value.

Postgres2015全国用户大会将于11月20至21日在北京丽亭华苑酒店召开。本次大会嘉宾阵容强大,国内顶级PostgreSQL数据库专家将悉数到场,并特邀欧洲、俄罗斯、日本、美国等国家和地区的数据库方面专家助阵:

  • Postgres-XC项目的发起人铃木市一(SUZUKI Koichi)
  • Postgres-XL的项目发起人Mason Sharp
  • pgpool的作者石井达夫(Tatsuo Ishii)
  • PG-Strom的作者海外浩平(Kaigai Kohei)
  • Greenplum研发总监姚延栋
  • 周正中(德哥), PostgreSQL中国用户会创始人之一
  • 汪洋,平安科技数据库技术部经理
  • ……
 
  • 2015年度PG大象会报名地址:http://postgres2015.eventdove.com/
  • PostgreSQL中国社区: http://postgres.cn/
  • PostgreSQL专业1群: 3336901(已满)
  • PostgreSQL专业2群: 100910388
  • PostgreSQL专业3群: 150657323



使用单精或双精类型时, 我们查询出来的值可能与真实存储的值有一定差别.

这里体现了眼见不为实的特征.
以下是float4, float8的输出函数.
src/backend/utils/adt/float.c

/*
 *              float4out               - converts a float4 number to a string
 *                                                using a standard output format
 */
Datum
float4out(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
 */
Datum
float8out(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
 */
Datum
numeric_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 TABLE
postgres=# \d t3
           Table "public.t3"
 Column |       Type       | Modifiers 
--------+------------------+-----------
 c1     | double precision | 
 c2     | numeric          | 

postgres=# insert into t3 values (1.55555555555555555555555555555555555, 1.55555555555555555555555555555555555);
INSERT 0 1
postgres=# 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来存储, 以免出现以上问题.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值