Skip to content

Commit

Permalink
Handle sub-second representation error
Browse files Browse the repository at this point in the history
According to ?`%/%`:

'%%' and 'x %/% y' can be used for non-integer 'y', e.g. '1 %/% 0.2',
but the results are subject to representation error and so may be
platform-dependent.

The example below shows one consequence of this.  The 'x' object has
an observation every 100ms, so we expect endpoints(x, "ms", 200) to
produce a sequence from 0 to 10 in steps of 2.  But the 4th observation
is 7 instead of 6, the cause of which you can see from the output of
sprintf():

  R> x <- .xts(1:10, .POSIXct(1.5e9 + 0:9 / 10))
  R> endpoints(x, "ms", 200)
  [1]  0  2  4  7  8 10
  R> sprintf("%0.0f", .index(x) %/% 1e-3)
   [1] "1500000000000" "1500000000099" "1500000000200" "1500000000300"
   [5] "1500000000400" "1500000000500" "1500000000599" "1500000000700"
   [9] "1500000000800" "1500000000900"

The output is more reasonable if we convert to milliseconds and
microseconds by multiplying instead of integer division by a fraction.

  R> sprintf("%0.0f", .index(x) * 1e3)
   [1] "1500000000000" "1500000000100" "1500000000200" "1500000000300"
   [5] "1500000000400" "1500000000500" "1500000000600" "1500000000700"
   [9] "1500000000800" "1500000000900"

We don't need to worry about values to the right of the decimal place,
because the cast to 64-bit integer in the C code will truncate the
double values toward zero (according to C99).

See #202.
  • Loading branch information
joshuaulrich committed Jul 29, 2017
1 parent 2665785 commit bb08948
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 2 deletions.
6 changes: 4 additions & 2 deletions R/endpoints.R
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,13 @@ function(x,on='months',k=1) {
},
"ms" = ,
"milliseconds" = {
.Call("endpoints", .index(x)%/%.001, 1L, k, addlast, PACKAGE='xts')
sec2ms <- .index(x) * 1e3
.Call("endpoints", sec2ms, 1L, k, addlast, PACKAGE='xts')
},
"us" = ,
"microseconds" = {
.Call("endpoints", .index(x)%/%.000001, 1L, k, addlast, PACKAGE='xts')
sec2us <- .index(x) * 1e6
.Call("endpoints", sec2us, 1L, k, addlast, PACKAGE='xts')
}
)
}
Expand Down
12 changes: 12 additions & 0 deletions inst/unitTests/runit.endpoints.R
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,15 @@ test.sub_second_resolution <- function() {
ep <- endpoints(x, "ms", 250)
checkIdentical(ep, c(0L, 3L, 5L, 6L))
}

# precision issues
test.sub_second_resolution_exact <- function() {
x <- .xts(1:6, .POSIXct(0:5 / 10))
ep <- endpoints(x, "ms", 250)
checkIdentical(ep, c(0L, 3L, 5L, 6L))
}
test.sub_second_resolution_representation <- function() {
x <- .xts(1:10, .POSIXct(1.5e9 + 0:9 / 10))
ep <- endpoints(x, "ms", 200)
checkIdentical(ep, seq(0L, 10L, 2L))
}

0 comments on commit bb08948

Please sign in to comment.