2017-02-09 235 views
2

我的印象是,PostgreSQL在时间戳中舍入了半微秒,甚至达到了最接近的微秒。例如: -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) 

回答

1

All the PostgreSQL time types有一个微秒的分辨率,六个小数位。 舍入到最接近的偶数微秒不会是微秒的分辨率。

它的行为看起来与我一致round half-up,通常的方式来回合。 > = 0.5,否则向下。

0.5024585圆角半多达6位小数四舍五入到0.502459,因为第7位是5

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 
+0

绝对不是。我查看了PostgreSQL的源代码,并使用strtod转换为double。 – ifotneak