R 使用as.POSIXlt时的舍入误差(微秒)

R 使用as.POSIXlt时的舍入误差(微秒),r,datetime,time,posixct,R,Datetime,Time,Posixct,我在as.POSIXlt中发现了一些我无法解释的奇怪行为,我希望其他人也能解释。在调查中,我发现有时一秒钟的小数部分会被错误地四舍五入 例如,下面的数字表示自历元开始以来的特定秒数,最后6位数字是秒数的小数部分,因此第一个数字上的秒数应为.645990 # Generate sequence of integers to represent date/times times <- seq( 1366039619645990 , length.out = 11 ) options(scipe

我在
as.POSIXlt
中发现了一些我无法解释的奇怪行为,我希望其他人也能解释。在调查中,我发现有时一秒钟的小数部分会被错误地四舍五入

例如,下面的数字表示自历元开始以来的特定秒数,最后6位数字是秒数的小数部分,因此第一个数字上的秒数应为.645990

# Generate sequence of integers to represent date/times
times <- seq( 1366039619645990 , length.out = 11 )
options(scipen=20)
times
 [1] 1366039619645990 1366039619645991 1366039619645992 1366039619645993 1366039619645994 1366039619645995
 [7] 1366039619645996 1366039619645997 1366039619645998 1366039619645999 1366039619646000

# Convert to date/time with microseconds 
options(digits.secs = 6 )
as.POSIXlt( times/1e6, tz="EST", origin="1970-01-01") + 5e-7
 [1] "2013-04-15 10:26:59.645990 EST" "2013-04-15 10:26:59.645991 EST" "2013-04-15 10:26:59.645992 EST"
 [4] "2013-04-15 10:26:59.645993 EST" "2013-04-15 10:26:59.645994 EST" "2013-04-15 10:26:59.645995 EST"
 [7] "2013-04-15 10:26:59.645996 EST" "2013-04-15 10:26:59.645997 EST" "2013-04-15 10:26:59.645998 EST"
[10] "2013-04-15 10:26:59.645999 EST" "2013-04-15 10:26:59.646000 EST"
as.POSIXlt
返回的向量中的第10个元素与上述等效的单个元素进行比较。发生了什么事

会话信息:

R version 2.15.2 (2012-10-26)
Platform: x86_64-apple-darwin9.8.0/x86_64 (64-bit)

locale:
[1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] raster_2.0-41 sp_1.0-5     

loaded via a namespace (and not attached):
[1] grid_2.15.2     lattice_0.20-13 tools_2.15.2

我没有得到正确的答案(继续研究),但我觉得这很有趣:

times <- seq( 1366039619645990 , length.out = 11 )
# Convert to date/time wz="EST", origin="1970-01-01") + 5e-7
options(digits.secs = 6 )

test <- as.POSIXlt( times/1e6, tz="EST", origin="1970-01-01") + 5e-7

test1[1] <- NULL
for(i in 1:11)
  test1[i] <- as.POSIXlt(times[i]/1e6, tz="EST", origin="1970-01-01") + 5e-7

> identical(test, test1)
[1] TRUE

查看最后两条语句,这个问题似乎主要与显示单个值有关,而不是向量。但即使在这种情况下,它也可能是一个截断,可能是通过
地板
,而不是舍入。

这似乎是一个舍入问题,即舍弃小数秒的有效数字。有问题的(?)代码位于类
POSIXlt
的对象的格式化方法中,即
format.POSIXlt
print.POSIXlt
使用

如果我们以下面的两个值为例,
format.POSIXlt
使用我用sapply包装的下面一行来测试小数秒之间的差值的绝对值,该差值依次舍入到更大的位数

secs <- c( 59.645998 , 59.645999 )
sapply( seq_len(np) - 1L , function(x) abs(secs - round(secs, x)) )
         [,1]     [,2]     [,3]     [,4]     [,5]     [,6]
[1,] 0.354002 0.045998 0.004002 0.000002 0.000002 0.000002
[2,] 0.354001 0.045999 0.004001 0.000001 0.000001 0.000001
因此,由于四舍五入中使用的测试,小数秒被截断为小数点后3位。我认为如果for循环中的测试值设置为5e-7,这个问题就会消失


当返回的结果是
POSIXlt
对象的向量时,必须调用不同的打印方法。

在我的机器上,执行
次/1e6
时,它会删除
次的所有小数。事实上,我得到了美国东部时间2013-04-15 10:26:59的
11次…@Michele谢谢您已经向我展示了我在示例中缺少一段重要的代码-立即添加@米歇尔,你能再试一次吗?首先运行
options(digits.secs=6)
。这是一个格式问题,讨论得最透彻。@JoshuaUlrich感谢您的链接-非常有用。那我就把它当傻瓜关上好吗?我在看POSIXlt,这就是为什么我没有看到这个标题与POSIXct+1,让我看到它必须是一个四舍五入或截断的问题
> test
 [1] "2013-04-15 10:26:59.645990 EST" "2013-04-15 10:26:59.645991 EST" "2013-04-15 10:26:59.645992 EST"
 [4] "2013-04-15 10:26:59.645993 EST" "2013-04-15 10:26:59.645994 EST" "2013-04-15 10:26:59.645995 EST"
 [7] "2013-04-15 10:26:59.645996 EST" "2013-04-15 10:26:59.645997 EST" "2013-04-15 10:26:59.645998 EST"
[10] "2013-04-15 10:26:59.645999 EST" "2013-04-15 10:26:59.646000 EST"
> test[10]
[1] "2013-04-15 10:26:59.645 EST"
> as.POSIXlt( times[10]/1e6, tz="EST", origin="1970-01-01") + 5e-7
[1] "2013-04-15 10:26:59.645 EST"
secs <- c( 59.645998 , 59.645999 )
sapply( seq_len(np) - 1L , function(x) abs(secs - round(secs, x)) )
         [,1]     [,2]     [,3]     [,4]     [,5]     [,6]
[1,] 0.354002 0.045998 0.004002 0.000002 0.000002 0.000002
[2,] 0.354001 0.045999 0.004001 0.000001 0.000001 0.000001
# the number of digits used for the fractional seconds is gotten here
np <- getOption("digits.secs")

# and the length of digits to be printed is controlled in this loop
for (i in seq_len(np) - 1L) if (all(abs(secs - round(secs, 
                i)) < 0.000001)) {
                np <- i
                break
            }
sprintf( "%.20f" , abs(secs[2] - round(secs,5)))
[1] "0.00000099999999991773"            

# In turn this is used to control the printing of the fractional seconds            
if (np == 0L) 
            "%Y-%m-%d %H:%M:%S"
        else paste0("%Y-%m-%d %H:%M:%OS", np)