how to return number of decimal places in R

前端 未结 12 1109
走了就别回头了
走了就别回头了 2020-12-01 08:25

I am working in R. I have a series of coordinates in decimal degrees, and I would like to sort these coordinates by how many decimal places these numbers have (i.e. I will w

相关标签:
12条回答
  • 2020-12-01 08:48

    Here is one way. It checks the first 20 places after the decimal point, but you can adjust the number 20 if you have something else in mind.

    x <- pi
    match(TRUE, round(x, 1:20) == x)
    

    Here is another way.

    nchar(strsplit(as.character(x), "\\.")[[1]][2])
    
    0 讨论(0)
  • 2020-12-01 08:50

    Interesting question. Here is another tweak on the above respondents' work, vectorized, and extended to handle the digits on the left of the decimal point. Tested against negative digits, which would give an incorrect result for the previous strsplit() approach.

    If it's desired to only count the ones on the right, the trailingonly argument can be set to TRUE.

    nd1 <- function(xx,places=15,trailingonly=F) {
      xx<-abs(xx); 
      if(length(xx)>1) {
        fn<-sys.function();
        return(sapply(xx,fn,places=places,trailingonly=trailingonly))};
      if(xx %in% 0:9) return(!trailingonly+0); 
      mtch0<-round(xx,nds <- 0:places); 
      out <- nds[match(TRUE,mtch0==xx)]; 
      if(trailingonly) return(out); 
      mtch1 <- floor(xx*10^-nds); 
      out + nds[match(TRUE,mtch1==0)]
    }
    

    Here is the strsplit() version.

    nd2 <- function(xx,trailingonly=F,...) if(length(xx)>1) {
      fn<-sys.function();
      return(sapply(xx,fn,trailingonly=trailingonly))
      } else {
        sum(c(nchar(strsplit(as.character(abs(xx)),'\\.')[[1]][ifelse(trailingonly, 2, T)]),0),na.rm=T);
      }
    

    The string version cuts off at 15 digits (actually, not sure why the other one's places argument is off by one... the reason it's exceeded through is that it counts digits in both directions so it could go up to twice the size if the number is sufficiently large). There is probably some formatting option to as.character() that can give nd2() an equivalent option to the places argument of nd1().

    nd1(c(1.1,-8.5,-5,145,5,10.15,pi,44532456.345243627,0));
    # 2  2  1  3  1  4 16 17  1
    nd2(c(1.1,-8.5,-5,145,5,10.15,pi,44532456.345243627,0));
    # 2  2  1  3  1  4 15 15  1
    

    nd1() is faster.

    rowSums(replicate(10,system.time(replicate(100,nd1(c(1.1,-8.5,-5,145,5,10.15,pi,44532456.345243627,0))))));
    rowSums(replicate(10,system.time(replicate(100,nd2(c(1.1,-8.5,-5,145,5,10.15,pi,44532456.345243627,0))))));
    
    0 讨论(0)
  • 2020-12-01 08:51

    If someone here needs a vectorized version of the function provided by Gergely Daróczi above:

    decimalplaces <- function(x) {
      ifelse(abs(x - round(x)) > .Machine$double.eps^0.5,
             nchar(sub('^\\d+\\.', '', sub('0+$', '', as.character(x)))),
             0)
    }
    
    decimalplaces(c(234.1, 3.7500, 1.345, 3e-15))
    #> 1 2 3 0
    
    0 讨论(0)
  • 2020-12-01 08:52

    You could write a small function for the task with ease, e.g.:

    decimalplaces <- function(x) {
        if ((x %% 1) != 0) {
            nchar(strsplit(sub('0+$', '', as.character(x)), ".", fixed=TRUE)[[1]][[2]])
        } else {
            return(0)
        }
    }
    

    And run:

    > decimalplaces(23.43234525)
    [1] 8
    > decimalplaces(334.3410000000000000)
    [1] 3
    > decimalplaces(2.000)
    [1] 0
    

    Update (Apr 3, 2018) to address @owen88's report on error due to rounding double precision floating point numbers -- replacing the x %% 1 check:

    decimalplaces <- function(x) {
        if (abs(x - round(x)) > .Machine$double.eps^0.5) {
            nchar(strsplit(sub('0+$', '', as.character(x)), ".", fixed = TRUE)[[1]][[2]])
        } else {
            return(0)
        }
    }
    
    0 讨论(0)
  • 2020-12-01 08:52

    I have tested some solutions and I found this one robust to the bugs reported in the others.

    countDecimalPlaces <- function(x) {
      if ((x %% 1) != 0) {
        strs <- strsplit(as.character(format(x, scientific = F)), "\\.")
        n <- nchar(strs[[1]][2])
      } else {
        n <- 0
      }
      return(n) 
    }
    
    # example to prove the function with some values
    xs <- c(1000.0, 100.0, 10.0, 1.0, 0, 0.1, 0.01, 0.001, 0.0001)
    sapply(xs, FUN = countDecimalPlaces)
    
    0 讨论(0)
  • 2020-12-01 08:55

    For the common application, here's modification of daroczig's code to handle vectors:

    decimalplaces <- function(x) {
        y = x[!is.na(x)]
        if (length(y) == 0) {
          return(0)
        }
        if (any((y %% 1) != 0)) {
          info = strsplit(sub('0+$', '', as.character(y)), ".", fixed=TRUE)
          info = info[sapply(info, FUN=length) == 2]
          dec = nchar(unlist(info))[seq(2, length(info), 2)]
          return(max(dec, na.rm=T))
        } else {
          return(0)
        }
    }
    

    In general, there can be issues with how a floating point number is stored as binary. Try this:

    > sprintf("%1.128f", 0.00000000001)
    [1] "0.00000000000999999999999999939458150688409432405023835599422454833984375000000000000000000000000000000000000000000000000000000000"
    

    How many decimals do we now have?

    0 讨论(0)
提交回复
热议问题