*! _kmplot_times Version 1.0.0 2026/10/20 *! Expand numeric time lists without rounding explicit double values *! Author: Timothy P Copeland, Karolinska Institutet *! Program class: rclass /* Returns r(times): the requested times, sorted, duplicates dropped, each in the shortest decimal text that reads back as the same double. Typed values keep their full double precision. A member generated by a range (start + j * step, j > 0) carries the arithmetic error of that sum (3 * 0.1 = 0.30000000000000004); it is replaced by its 15-significant-digit value, the decimal the range stands for, so 0(0.1)1 and the typed list 0 .1 ... 1 give the same doubles. With stmatch, also returns r(cmptimes): for each time in r(times), the value to compare with _t. When the stset base time variable is stored as float and stset's transform _t = (time - origin) / scale (constant origin and scale) reproduces _t on every event record, a requested time tp is compared at the data's float precision: (float(tp * scale + origin) - origin) / scale, which is exactly the _t of a record whose time variable holds float(tp). Otherwise r(cmptimes) equals r(times). */ program define _kmplot_times, rclass version 16.0 local _orig_varabbrev = c(varabbrev) set varabbrev off capture noisily { syntax , VALues(string) [STMatch] * Stata validates grammar and cardinality; its decimal serialization * is used only to count sequence members, never as the requested time. numlist "`values'" tokenize "`values'", parse(" /()[]:") local times "" tempname start step value v15 while "`1'" != "" { scalar `start' = `1' scalar `step' = 1 local n = 1 if inlist("`2'", "(", "[") { scalar `step' = `3' numlist "`1'(`3')`5'" local n : word count `r(numlist)' macro shift 5 } else if "`2'" == "/" { numlist "`1'/`3'" local n : word count `r(numlist)' if `3' < scalar(`start') scalar `step' = -1 macro shift 3 } else if inlist("`3'", ":", "to") { scalar `step' = `2' - scalar(`start') numlist "`1' `2' to `4'" local n : word count `r(numlist)' macro shift 4 } else { macro shift } forvalues j = 0/`=`n' - 1' { scalar `value' = scalar(`start') + `j' * scalar(`step') if `j' > 0 { scalar `v15' = real(string(scalar(`value'), "%21.15g")) if abs(scalar(`v15') - scalar(`value')) <= /// 1e-9 * abs(scalar(`step')) { scalar `value' = scalar(`v15') } } local text : display %24.17g scalar(`value') local times "`times' `text'" } } mata: st_local("times", invtokens(strofreal(uniqrows(strtoreal(tokens(st_local("times")))')', "%24.17g"))) * Each value in the shortest of %21.15g, %21.16g and %24.17g that * reads back as the same double. local _short "" foreach _x of local times { local _sx "`_x'" forvalues _k = 16(-1)15 { local _try : display %21.`_k'g `_x' local _try = strtrim("`_try'") if real("`_try'") == `_x' local _sx "`_try'" } local _short "`_short' `_sx'" } local times = strtrim("`_short'") local cmptimes "`times'" if "`stmatch'" != "" { local _bt : char _dta[st_bt] local _o : char _dta[st_o] local _s : char _dta[st_s] local _map = 0 capture confirm numeric variable `_bt', exact if _rc == 0 { if "`: type `_bt''" == "float" { capture confirm number `_o' local _map = (_rc == 0) capture confirm number `_s' if _rc local _map = 0 } } if `_map' { quietly count if _d == 1 & _t != (`_bt' - (`_o')) / (`_s') if r(N) == 0 { mata: st_local("cmptimes", invtokens(strofreal( /// (floatround(strtoreal(tokens(st_local("times"))) /// :* strtoreal(st_local("_s")) :+ strtoreal(st_local("_o"))) /// :- strtoreal(st_local("_o"))) :/ strtoreal(st_local("_s")), /// "%24.17g"))) } } } } local rc = _rc set varabbrev `_orig_varabbrev' if `rc' exit `rc' return local times "`times'" if "`stmatch'" != "" return local cmptimes "`cmptimes'" end