1

I want to convert my many-digit numeric vector to character. I tried the following solutions here which works for one number but not for a vector. This is OK

options(digits=20)
options(scipen=99999)
x<-129483.19999999999709;format(round(x, 12), nsmall = 12)
[1] "129483.199999999997"

But this is not. how to keep numeric precision in characters for numeric vectors?

> y <- c(129483.19999999999709, 1.3546746874,687676846.2546746464)

Specially problematic is 687676846.2546746464 Also tried:

> specify_decimal(y, 12)
[1] "129483.199999999997"    "1.354674687400"         "687676846.254674673080"
> formatC(y, digits = 12, format = "f")
[1] "129483.199999999997"    "1.354674687400"         "687676846.254674673080"
> formattable(y, digits = 12, format = "f")
[1] 129483.199999999997    1.354674687400         687676846.254674673080
> sprintf(y, fmt='%#.12g')
[1] "129483.200000" "1.35467468740" "687676846.255"
> sprintf(y, fmt='%#.22g')
[1] "129483.1999999999970896" "1.354674687399999966075" "687676846.2546746730804"

Expected result:

[1] "129483.199999999997" "1.354674687400" "687676846.254674646400"

It seems that precision loss occurs once only, it is not repeated.

> require(dplyr)
> convert <- function(x) as.numeric(as.character(x))
> 687676846.2546746464 %>% convert
[1] 687676846.25467503
> 687676846.2546746464 %>% convert %>% convert %>% convert
[1] 687676846.25467503

Here I only have 5-digit precision, but more problematic I can't know beforehand what precision I am going to get..

gaut
  • 5,771
  • 1
  • 14
  • 45
  • 2
    `as.numeric(as.character(129483.123456789)) == 129483.123456789` prints `TRUE`. I think there are two issues here. First, what is printed to the console may not reflect the underlying precision. Second, your sample number is floating point, and due to rounding error there could be some issues there. If you check my example number, rounding won't be a problem. – Tim Biegeleisen Nov 13 '18 at 11:08
  • well, what about my number? – gaut Nov 13 '18 at 11:10
  • character strings should definitely show all the digits – gaut Nov 13 '18 at 11:11
  • What _about_ your number? It should behave the same as any number, expect that because your number has so many decimal places, there could be rounding issues happening here as well. – Tim Biegeleisen Nov 13 '18 at 11:12
  • ok well so my question is how to circumvent those rounding issues – gaut Nov 13 '18 at 11:12
  • as mentioned I need a character string with all digits – gaut Nov 13 '18 at 11:13
  • 2
    Check here:https://stackoverflow.com/questions/3443687/formatting-decimal-places-in-r, I think this code will do for your case: `x<-129483.19999999999709;format(round(x, 14), nsmall = 14)` – Antonios Nov 13 '18 at 11:19
  • thanks for the try, but see edits, it doesn't work for all numbers – gaut Nov 13 '18 at 20:52
  • 1
    If you are doing this because you want to write a number to a text file in such a way that you can recover exactly the same number when you read the text file, perhaps you could convert it to its binary representation (abbreviated as hex). – John Coleman Nov 14 '18 at 15:28
  • Thanks John, indeed this is part of the problem, I also need for some reason to have 15 trailing zeroes to ensure format is recognized during upload. will dig hex – gaut Nov 14 '18 at 15:31

1 Answers1

1

At the end I could do what I wanted using these functions. addtrailingzeroes will add a number of zeroes after decimal to x.

nbdec <- function(x) {
  x1 <- as.character(x)
  xsplit <- strsplit(x1,"\\.")
  xlength <- sapply(xsplit, function(d) nchar(d)[2])
  xlength <- ifelse(is.na(xlength), 0, xlength)
  return(xlength)
}

trailingzeroes <- function(x, dig) {
  res <- rep(NA, length(x))
  for( i in 1:length(x)) {
    if(!is.na(x[i])) res[i] <- { paste0(rep(0,max(0,dig-nbdec(x[i]))), collapse="") }
    else { res[i] <- ""}
    }
return(res)
}

trailingcommas <- function(x) ifelse(is.na(x), NA, ifelse(nbdec(x)==0, ".",""))

addtrailingzeroes <- function(x, digits) {
  return(ifelse(!is.na(x), paste0(x, trailingcommas(x), trailingzeroes(x, digits)),NA))
}

However to suppress inaccuracies and rounding mistakes, x has to be cropped first using roundnumerics.max:

roundnumerics.max <- function(df, startdig=12) {
  for(icol in 1:ncol(df)) {
    if( is.numeric(df[,icol])) {
      dig <- startdig
      while(any(!as.numeric(as.character(df[,icol])) %==% df[,icol])) {
        dig <- dig-1
        df[,icol] <- round(df[,icol], digits=dig)
        if(dig==0) {
          break
          pprint("ERROR: zero numeric accuracy")
        }
      } 
      pprint("Numeric accuracy for column ",icol," ", colnames(df)[icol], " is ", dig)
    }
  }
  return(data.frame(df, stringsAsFactors = F))
}

This is slow and far from elegant... I still think it hard to believe that R has such an accuracy limitation to 16 significant digits, and adds inaccurate noise that causes divergences when you try to increase the digits option...Without letting you know...

gaut
  • 5,771
  • 1
  • 14
  • 45