Skip to content

Commit ce1b667

Browse files
committed
Correct endpoints when index is before the epoch
The endpoints C code casts the double index to long, which truncates it toward zero. This behavior is desired when the index is positive, because it moves the endpoint *back* in time. But when the index is negative, truncating toward zero moves the endpoint *forward* in time. This is also an issue if the index value is stored as integer, since the C99 specification states that integer division truncates toward zero. If the first index value is less than zero, branch into a special case to handle pre-epoch index values. This avoids performance degradation if all index values are after the epoch. If the index value is less than zero, simply add 1 to offset the truncation toward zero. We also need to furthre adjust the potential endpoint value if the index is exactly equal to zero. Fixes #144.
1 parent d5d7c6a commit ce1b667

File tree

1 file changed

+58
-14
lines changed

1 file changed

+58
-14
lines changed

src/endpoints.c

+58-14
Original file line numberDiff line numberDiff line change
@@ -36,26 +36,70 @@ SEXP endpoints (SEXP _x, SEXP _on, SEXP _k, SEXP _addlast /* TRUE */)
3636
/*int_index = INTEGER(getAttrib(_x, install("index")));*/
3737
int_index = INTEGER(_x);
3838
ep[0] = 0;
39-
for(i=1,j=1; i<nr; i++) {
40-
int_tmp[0] = int_index[i] / on / k +1;
41-
int_tmp[1] = int_index[i-1] / on / k +1;
42-
if( (int_tmp[0] - int_tmp[1]) != 0 ) {
43-
ep[j] = i;
44-
j++;
45-
}
39+
/* special handling if index values < 1970-01-01 00:00:00 UTC */
40+
if(int_index[0] < 0) {
41+
for(i=1,j=1; i<nr; i++) {
42+
int idx_i0 = int_index[i];
43+
int idx_i1 = int_index[i-1];
44+
45+
// if index equals epoch
46+
int epoch_adj = (idx_i0 == 0);
47+
// cast truncates toward zero
48+
// (i.e. *forward* in time if index < 0)
49+
if(idx_i0 < 0) idx_i0++;
50+
if(idx_i1 < 0) idx_i1++;
51+
52+
int_tmp[0] = idx_i0 / on / k + epoch_adj;
53+
int_tmp[1] = idx_i1 / on / k;
54+
if( (int_tmp[0] - int_tmp[1]) != 0 ) {
55+
ep[j] = i;
56+
j++;
57+
}
58+
}
59+
} else {
60+
for(i=1,j=1; i<nr; i++) {
61+
int_tmp[0] = int_index[i] / on / k +1;
62+
int_tmp[1] = int_index[i-1] / on / k +1;
63+
if( (int_tmp[0] - int_tmp[1]) != 0 ) {
64+
ep[j] = i;
65+
j++;
66+
}
67+
}
4668
}
4769
break;
4870
case REALSXP:
4971
/*real_index = REAL(getAttrib(_x, install("index")));*/
5072
real_index = REAL(_x);
5173
ep[0] = 0;
52-
for(i=1,j=1; i<nr; i++) {
53-
real_tmp[0] = (long)real_index[i] / on / k +1;
54-
real_tmp[1] = (long)real_index[i-1] / on / k +1;
55-
if( (real_tmp[0] - real_tmp[1]) != 0 ) {
56-
ep[j] = i;
57-
j++;
58-
}
74+
/* special handling if index values < 1970-01-01 00:00:00 UTC */
75+
if(real_index[0] < 0) {
76+
for(i=1,j=1; i<nr; i++) {
77+
double idx_i0 = real_index[i];
78+
double idx_i1 = real_index[i-1];
79+
80+
// if index equals epoch
81+
int epoch_adj = (idx_i0 == 0);
82+
// cast truncates toward zero
83+
// (i.e. *forward* in time if index < 0)
84+
if(idx_i0 < 0) idx_i0 += 1.0;
85+
if(idx_i1 < 0) idx_i1 += 1.0;
86+
87+
real_tmp[0] = (long)(idx_i0 / on / k + epoch_adj);
88+
real_tmp[1] = (long)(idx_i1 / on / k);
89+
if( (real_tmp[0] - real_tmp[1]) != 0) {
90+
ep[j] = i;
91+
j++;
92+
}
93+
}
94+
} else {
95+
for(i=1,j=1; i<nr; i++) {
96+
real_tmp[0] = (long)real_index[i] / on / k +1;
97+
real_tmp[1] = (long)real_index[i-1] / on / k +1;
98+
if( (real_tmp[0] - real_tmp[1]) != 0 ) {
99+
ep[j] = i;
100+
j++;
101+
}
102+
}
59103
}
60104
break;
61105
default:

0 commit comments

Comments
 (0)