2

我的印象是 PostgreSQL 将时间戳中的半微秒四舍五入到最接近的偶数微秒。例如:

> select '2000-01-01T00:00:00.0000585Z'::timestamptz;
          timestamptz          
-------------------------------
 2000-01-01 01:00:00.000058+01
(1 row)

> select '2000-01-01T00:00:00.0000575Z'::timestamptz;
          timestamptz          

-------------------------------
 2000-01-01 01:00:00.000058+01
(1 row)

然后我发现:

> select '2000-01-01T00:00:00.5024585Z'::timestamptz;
          timestamptz          
-------------------------------
 2000-01-01 01:00:00.502459+01
(1 row)

有人知道 Postgresql 用于时间戳的舍入算法吗?

供您参考,这是我正在运行的 Postgresql 版本:

> select version();  
                                                version                                                     
----------------------------------------------------------------------------------------------------------------
 PostgreSQL 9.6.1 on x86_64-apple-darwin15.6.0, compiled by Apple LLVM version 8.0.0 (clang-800.0.42.1), 64-bit
(1 row)
4

1 回答 1

1

所有 PostgreSQL 时间类型都有一个微秒的分辨率,六个小数位。四舍五入到最接近的偶数微秒将不是微秒分辨率。

在我看来,它的行为与通常的舍入方式一致。>= 0.5 向上取整,否则向下取整。

0.5024585 舍入到小数点后 6 位,因为第 7 位是 5,所以舍入到 0.502459。

test=# select '2000-01-01T00:00:00.5024585Z'::timestamp;
         timestamp          
----------------------------
 2000-01-01 00:00:00.502459
(1 row)

0.5024584999999 向下舍入为 0.502458,因为第 7 位是 4。

test=# select '2000-01-01T00:00:00.5024584999999Z'::timestamp;
         timestamp          
----------------------------
 2000-01-01 00:00:00.502458
(1 row)


没关系,以上似乎是异常的。从 '2000-01-01T00:00:00.5024235Z' 到 '2000-01-01T00:00:00.5024355Z' 与半偶数舍入一致。

我猜测异常是由于浮点错误从输入中的浮点秒转换为timestamp使用的整数微秒。

test=# select '2000-01-01T00:00:00.5024235Z'::timestamp;
         timestamp          
----------------------------
 2000-01-01 00:00:00.502424
(1 row)

test=# select '2000-01-01T00:00:00.5024245Z'::timestamp;
         timestamp          
----------------------------
 2000-01-01 00:00:00.502425
(1 row)

test=# select '2000-01-01T00:00:00.5024255Z'::timestamp;
         timestamp          
----------------------------
 2000-01-01 00:00:00.502425
(1 row)

test=# select '2000-01-01T00:00:00.5024265Z'::timestamp;
         timestamp          
----------------------------
 2000-01-01 00:00:00.502426
(1 row)

test=# select '2000-01-01T00:00:00.5024275Z'::timestamp;
         timestamp          
----------------------------
 2000-01-01 00:00:00.502428
(1 row)

test=# select '2000-01-01T00:00:00.5024285Z'::timestamp;
         timestamp          
----------------------------
 2000-01-01 00:00:00.502428
(1 row)

test=# select '2000-01-01T00:00:00.5024295Z'::timestamp;
         timestamp         
---------------------------
 2000-01-01 00:00:00.50243
(1 row)

test=# select '2000-01-01T00:00:00.5024305Z'::timestamp;
         timestamp         
---------------------------
 2000-01-01 00:00:00.50243
(1 row)

test=# select '2000-01-01T00:00:00.5024315Z'::timestamp;
         timestamp          
----------------------------
 2000-01-01 00:00:00.502432
(1 row)

test=# select '2000-01-01T00:00:00.5024325Z'::timestamp;
         timestamp          
----------------------------
 2000-01-01 00:00:00.502432
(1 row)

test=# select '2000-01-01T00:00:00.5024335Z'::timestamp;
         timestamp          
----------------------------
 2000-01-01 00:00:00.502434
(1 row)

test=# select '2000-01-01T00:00:00.5024345Z'::timestamp;
         timestamp          
----------------------------
 2000-01-01 00:00:00.502434
(1 row)

test=# select '2000-01-01T00:00:00.5024355Z'::timestamp;
         timestamp          
----------------------------
 2000-01-01 00:00:00.502436
(1 row)

这也与interval N microsecond. 更少的小数位意味着更少的浮点错误。

test=# select interval '0.5 microsecond';
 interval 
----------
 00:00:00
(1 row)

test=# select interval '1.5 microsecond';
    interval     
-----------------
 00:00:00.000002
(1 row)

test=# select interval '2.5 microsecond';
    interval     
-----------------
 00:00:00.000002
(1 row)

test=# select interval '3.5 microsecond';
    interval     
-----------------
 00:00:00.000004
(1 row)

test=# select interval '4.5 microsecond';
    interval     
-----------------
 00:00:00.000004
(1 row)

test=# select interval '5.5 microsecond';
    interval     
-----------------
 00:00:00.000006
(1 row)

test=# select interval '6.5 microsecond';
    interval     
-----------------
 00:00:00.000006
(1 row)

一个小型 C 程序确认在小数点后 7 位的单精度浮点数存在浮点精度问题,这会影响舍入。

#include <math.h>
#include <stdio.h>

int main() {
    float nums[] = {
        0.5024235f,
        0.5024245f,
        0.5024255f,
        0.5024265f,
        0.5024275f,
        0.5024285f,
        0.5024295f,
        0.5024305f,
        NAN
    };

    for( int i = 0; !isnan(nums[i]); i++ ) {
        printf("%0.8f\n", nums[i]);
    }
}

这会产生:

0.50242352
0.50242448
0.50242549
0.50242651
0.50242752
0.50242847
0.50242949
0.50243050

而双打则没有问题。

0.50242350
0.50242450
0.50242550
0.50242650
0.50242750
0.50242850
0.50242950
0.50243050
于 2017-02-09T23:37:11.123 回答