Search code examples
rdecimalradixfractions

How to convert a binary fraction number into decimal fraction number in R?


I need to write a function that converts a binary fraction number into decimal fraction number in R. e.g. f(0.001) # 0.125

What I did: I searched for the related functions in R packages:

DescTools::BinToDec(0.001) # NA
DescTools::BinToDec("0.001") # NA
base::strtoi(0.001, base=2) # NA
base::strtoi("0.001", base=2) # NA

base::packBits(intToBits(0.001), "integer") # 0
base::packBits(intToBits("0.001"), "integer") # 0

compositions::unbinary(0.001) # 0.001
compositions::unbinary("0.001") # NA

I searched in SOF, found the following:

base2decimal <- function(base_number, base = 2) {
  split_base <- strsplit(as.character(base_number), split = "")
  return(sapply(split_base, function(x) sum(as.numeric(x) * base^(rev(seq_along(x) - 1)))))}
base2decimal(0.001) # NA
base2decimal("0.001") # NA

0.001 is:

(0 * 2^(-1)) + (0 * 2^(-2)) + (1 * 2^(-3)) # 0.125
(0 * 1/2) + (0 * (1/2)^2) + (1 * (1/2)^3) # 0.125
(0 * 0.5) + (0 * (0.5)^2) + (1 * 0.5^3) # 0.125

So, something like sum of the inner product (0,0,1) * (0.5^1, 0.5^2, 0.5^3) seems to finish the problem, I could not figure out how to do this in general case.

javascript case:
How to convert a binary fraction number into decimal fraction number? How to convert binary fraction to decimal lisp case:
Convert fractions from decimal to binary


Solution

  • You can extend the solution you posted in your question to also include the negative powers of two starting at the position of the decimal separator as follows:

    base2decimal <- function(base_number, base = 2) {
        base_number = paste(as.character(base_number), ".", sep = "")
        return (mapply(function (val, sep) {
                          val = val[-sep];
                          if (val[[1]] == "-") {
                              sign = -1
                              powmax = sep[[1]] - 3
                              val = val[-1]
                          } else {
                              sign = 1
                              powmax = sep[[1]] - 2
                          };
                          sign * sum(as.numeric(val) * (base ^ seq(powmax, by = -1, length = length(val))))},
            strsplit(base_number, NULL), gregexpr("\\.", base_number)))
    }
    

    This code also works for other bases less than (or equal) 10:

    base2decimal(c('0.101', '.101', 0.101, 1101.001, 1101, '-0.101', '-.101', -0.101, -1101.001, -1101))
    #[1]   0.625   0.625   0.625  13.125  13.000  -0.625  -0.625  -0.625 -13.125
    #[10] -13.000
    base2decimal(1110.111)
    # 14.875
    base2decimal(256.3, 8)
    # [1] 174.375