From m@ech|er @end|ng |rom @t@t@m@th@ethz@ch  Thu Mar  3 17:52:40 2022
From: m@ech|er @end|ng |rom @t@t@m@th@ethz@ch (Martin Maechler)
Date: Thu, 3 Mar 2022 17:52:40 +0100
Subject: [Rd] Floating Point with POSIXct
In-Reply-To: <CAFsq6G_cp=6pyh7CEe=rLUK47i+9LqtTyyK5zwjsdcNKnVigVg@mail.gmail.com>
References: <CAFsq6G_cp=6pyh7CEe=rLUK47i+9LqtTyyK5zwjsdcNKnVigVg@mail.gmail.com>
Message-ID: <25120.62040.753761.512280@stat.math.ethz.ch>

>>>>> John Muschelli 
>>>>>     on Thu, 3 Mar 2022 11:04:05 -0500 writes:
>>>>> John Muschelli 
>>>>>     on Thu, 3 Mar 2022 11:04:05 -0500 writes:

    > I see in ?POSIXct and I'm trying to understand the note:
    >> Classes "POSIXct" and "POSIXlt" are able to express fractions of a second. (Conversion of fractions between the two forms may not be exact, but will have better than microsecond accuracy.)

    > Mainly, I'm trying to understand printing of POSIXct with fractional
    > seconds.  I see print.POSIXct calls format.POSIXct and eventually
    > calls format.POSIXlt, which then takes into account `digits.secs` for
    > printing. The format uses %OS3, which strptime indicates (* added):

    >> Specific to R is %OSn, which for output gives the seconds *truncated* to 0 <= n <= 6 decimal places (and if %OS is not followed by a digit, it uses the setting of getOption("digits.secs"), or if that is unset, n = 0).

    > So I'm seeing it truncates the seconds to 3 digits, so I think that is
    > why the below is printing 0.024.

    > I think this is especially relevant even if you set
    > `options(digits.secs = 6)`, then the code in
    > format.POSIXlt would still return np=3 as the following condition
    > would break at i = 3

    > for (i in seq_len(np) - 1L) 
    >   if (all(abs(secs - round(secs, > i)) < 1e-06)) {
    >     np <- i
    >     break
    > }

    > as sub_seconds - round(sub_seconds,3) < 1e-06.   This seems to be
    > expected behavior given the docs, but would any consider this a bug?


    > Example:

    > options(digits.secs = 4)
    > x = structure(947016000.025, class = c("POSIXct", "POSIXt"), tzone = "UTC")

I think you've fallen into the R FAQ 7.31 trap :

> ct <- 947016000.025
> ct %% 1
[1] 0.02499998
>

Of course, the issue may still be somewhat interesting, ...

Yes, POSIXct is of limited precision and I think the help page
you mentioned did document that that's one reason for using
POSIXlt instead, as there, sub second accuracy can be much better.

But FAQ 7.31 and the fact that all numbers are base 2 and in
base 2,  no decimal   <n>.025   can be represented in full accuracy.

Also, as you've noticed the R POSIX[cl]t  code just truncates,
i.e. rounds towards 0 unconditionally, and I tend to agree that it
should rather round than truncate.

But we should carefully separate the issues here, from the
underlying inherent FAQ 7.31 truth that most decimal numbers in
a computer are not quite what they look like ...

Martin Maechler
ETH Zurich and  R Core Team (also author of the CRAN package 'round')


    > summary(x, digits = 20)
    > #>                      Min.                   1st Qu.                    Median
    > #> "2000-01-04 20:00:00.024" "2000-01-04 20:00:00.024" "2000-01-04 20:00:00.024"
    > #>                      Mean                   3rd Qu.                      Max.
    > #> "2000-01-04 20:00:00.024" "2000-01-04 20:00:00.024" "2000-01-04 20:00:00.024"
    > x
    > #> [1] "2000-01-04 20:00:00.024 UTC"
    > format.POSIXct(x, format = "%Y-%m-%d %H:%M:%OS3")
    > #> [1] "2000-01-04 20:00:00.024"
    > format.POSIXct(x, format = "%Y-%m-%d %H:%M:%OS4")
    > #> [1] "2000-01-04 20:00:00.0249"
    > sub_seconds = as.numeric(x) %% 1
    > sub_seconds
    > #> [1] 0.02499998
    > round(sub_seconds, 3)
    > #> [1] 0.025

    > rounded = as.POSIXct(
    > floor(as.numeric(x)) +
    > round(as.numeric(x) %% 1, 3),
    > origin = "1970-01-01")
    > rounded
    > #> [1] "2000-01-04 20:00:00.024 UTC"
    > as.numeric(rounded) %% 1
    > #> [1] 0.02499998

    > R.version
    > _
    > platform       x86_64-pc-linux-gnu
    > arch           x86_64
    > os             linux-gnu
    > system         x86_64, linux-gnu
    > status
    > major          4
    > minor          1.2
    > year           2021
    > month          11
    > day            01
    > svn rev        81115
    > language       R
    > version.string R version 4.1.2 (2021-11-01)
    > nickname       Bird Hippie



    > Best,
    > John

    > ______________________________________________
    > R-devel at r-project.org mailing list
    > https://stat.ethz.ch/mailman/listinfo/r-devel



[Rd] Floating Point with POSIXct
Martin Maechler