2

I am trying to make a simple function to round decimals between 1.0 to 5.0 with my specification. In this case, the input numeric with ,

  • decimal element less than 0.3 should be round to 0 (floor)
  • decimal element greater than 0.7 should be rounded up (ceiling)
  • decimal element between 0.3 and 0.7 should be rounded to 0.5

I have the following function but it behavies weirdly. Check out the example I have posted. It behaves differently for 1 and c(2, 3, 4).

my_roundup <- function(x) {
  x <- as.numeric(format(x, digits = 2))
  stopifnot(x >= 1.0 & x <= 5.0) 
  floor(x) + (x %% 1 > 0.3)*0.5 + (x %% 1 > 0.7)*0.5
}

# e.g
my_roundup(x = 1.3) # 1.5
my_roundup(x = 2.3) # 2
my_roundup(x = 3.3) # 3
my_roundup(x = 4.3) # 4

The final output is as follows. I have inserted the expected rows with ####. Can anyone tell me what I am doing wrong ?

 (x <- seq(1, 5, 0.1))
cbind(actual = x, converted = my_roundup(x))

actual converted
 [1,]    1.0       1.0
 [2,]    1.1       1.0
 [3,]    1.2       1.0
 #####   1.3       1.0
 [4,]    1.3       1.5
 [5,]    1.4       1.5
 [6,]    1.5       1.5
 [7,]    1.6       1.5
 [8,]    1.7       1.5
 [9,]    1.8       2.0
[10,]    1.9       2.0
[11,]    2.0       2.0
[12,]    2.1       2.0
[13,]    2.2       2.0
[14,]    2.3       2.0
[15,]    2.4       2.5
[16,]    2.5       2.5
[17,]    2.6       2.5
#####    2.7       2.5
[18,]    2.7       3.0
[19,]    2.8       3.0
[20,]    2.9       3.0
[21,]    3.0       3.0
[22,]    3.1       3.0
[23,]    3.2       3.0
[24,]    3.3       3.0
[25,]    3.4       3.5
[26,]    3.5       3.5
[27,]    3.6       3.5
#####    3.7       3.5
[28,]    3.7       4.0
[29,]    3.8       4.0
[30,]    3.9       4.0
[31,]    4.0       4.0
[32,]    4.1       4.0
[33,]    4.2       4.0
[34,]    4.3       4.0
[35,]    4.4       4.5
[36,]    4.5       4.5
[37,]    4.6       4.5
#####    4.7       4.5
[38,]    4.7       5.0
[39,]    4.8       5.0
[40,]    4.9       5.0
[41,]    5.0       5.0
  • 1
    I think this is a case of [R FAQ 7.31](https://cran.r-project.org/doc/FAQ/R-FAQ.html#Why-doesn_0027t-R-think-these-numbers-are-equal_003f). It might be informative to see `(1:4+0.3) %% 1 - 0.3`, where you might expect `0 0 0 0`. – r2evans Aug 21 '18 at 14:51
  • 2
    Possible duplicate of [Why are these numbers not equal?](https://stackoverflow.com/questions/9508518/why-are-these-numbers-not-equal) – Rui Barradas Aug 21 '18 at 14:54

2 Answers2

2

This is indeed likely due to the representation of floating point numbers. Internally, 2.7 %% 2 could be represented something like 2.7000000000001

Since you format your input as single decimal number anyway, you could also do that for the modulo's

my_roundup <- function(x) {
  x <- as.numeric(format(x, digits = 2))
  stopifnot(x >= 1.0 & x <= 5.0) 
  floor(x) + (round(x %% 1,1) > 0.3)*0.5 + (round(x %% 1,1) > 0.7)*0.5
}
P1storius
  • 917
  • 5
  • 12
0

Here is a simpler version of the function:

my_roundup <- function(x) {
temp <- ifelse(round(x%%floor(x),1) < 0.3, 0, ifelse(round(x%%floor(x),1) <=0.7, 0.5, 1))
return(floor(x) + temp)
}
SmitM
  • 1,366
  • 1
  • 8
  • 14