[Rd] round() doesn't (PR#1139)
brahm@alum.mit.edu
brahm@alum.mit.edu
Thu, 17 Jan 2002 00:09:32 +0100 (MET)
Hi, all,
In PR#1138 and PR#1139, I pointed out that round() with digits<0 would not
actually round to an integer. Brian D. Ripley (hereafter BDR) fixed it in
R-1.4.0, but the fix introduced a new error, PR#1254 (caught by Ole
Christensen). BDR (I'm guessing) fixed the fix; here's the relevant line from
fround.c:
if(dig <= 0 && fabs(res) < 1e9) res = (int)floor(res + 0.5);
This solution casts the result as int if it's under a billion, but it
re-introduces the original problem for large numbers:
R-patched 01/13/2002> round(1e15, -2) - 1e15
[1] -0.25
So I'd ask to re-open PR#1139, and suggest another look at the "fround" C
function that I posted (in a followup) on 10/24/2001 (copied below). I
substituted it into src/nmath/fround.c, successfully re-built R, and it works.
It handles the 3 cases dig=0, dig>0, and dig<0 separately, avoids the roundoff
errors that were occurring for dig<0, and avoids a call to R_pow_di altogether
when dig=0. It gets the correct answer "0" in the above example.
Here are a few explicit test cases; the last one fails in R-patched 01/13/2002,
but all work with my "fround".
all.equal(round(1234.5678, 2), 1234.57)
all.equal(round(1234.5678, 0), 1235)
all.equal(round(1234.5678, -2), 1200)
all.equal(round(100000/3, -2) - 33300, 0)
all.equal(round(1e15, -2) - 1e15, 0) # Fails in R-patched
Thanks to all for a great program, and congratulations on the release of 1.4.0!
---- begin C code for fround.c: ----
double fround(double x, double digits) {
#define MAX_DIGITS DBL_MAX_10_EXP
/* = 308 (IEEE); was till R 0.99: (DBL_DIG - 1) */
/* Note that large digits make sense for very small numbers */
double pow10, sgn, intx;
int dig;
#ifdef IEEE_754
if (ISNAN(x) || ISNAN(digits))
return x + digits;
if(!R_FINITE(x)) return x;
#endif
if (digits > MAX_DIGITS)
digits = MAX_DIGITS;
dig = (int)floor(digits + 0.5);
if(x < 0.) {
sgn = -1.;
x = -x;
} else
sgn = 1.;
if (dig == 0) {
return sgn * R_rint(x);
} else if (dig > 0) {
pow10 = R_pow_di(10., dig);
intx = floor(x);
return sgn * (intx + R_rint((x-intx) * pow10) / pow10);
} else {
pow10 = R_pow_di(10., -dig);
return sgn * R_rint(x/pow10) * pow10;
}
}
---- end C code ----
-- David Brahm (brahm@alum.mit.edu)
-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
r-devel mailing list -- Read http://www.ci.tuwien.ac.at/~hornik/R/R-FAQ.html
Send "info", "help", or "[un]subscribe"
(in the "body", not the subject !) To: r-devel-request@stat.math.ethz.ch
_._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._