1

How to convert a base2 number (with fractional part) to a base10 number in R? The number can be negative as well.

Examples:

from2to10(10100101) # "165"
from2to10(0) # "0"
from2to10(10100101.01) # "165.25"
from2to10(-10100101) # "-165"
from2to10(-10100101.01) # "-165.25"
from2to10(111101111.010111) # "495.359375"
Erdogan CEVHER
  • 1,788
  • 1
  • 21
  • 40

2 Answers2

2

Edit: I realize I was assuming that the input would be character, perhaps a bad assumption on my part. I believe trusting R to preserve all of your 0s and 1s (with R FAQ 7.31 in mind) is a bit trusting, but I'll keep my answer as-is unless/until something better comes along.


This was interesting ... not certain if there's an R function that deals with floating-point in non-decimal, so here's one ...

#' Convert floating-point binary to decimal
#'
#' @param s 'character'
#' @return 'numeric'
#' @examples
#' tests <- c("10100101", "0", "10100101.01", "-10100101", "-10100101.01", "111101111.010111")
#' base2float(tests)
#' # [1]  165.0000    0.0000  165.2500 -165.0000 -165.2500  495.3594
base2float <- function(s, base = 2L) {
  # ensure the strings seem logical:
  # - start with "-", "+", or "[01]"
  # - zero or more "[01]"
  # - optional decimal "." (can easily change to "," for alternate reps)
  # - zero or more "[01]"
  stopifnot(all(grepl("^[-+]?[01]*\\.?[01]*$", s)))
  splits <- strsplit(s, "\\.")
  wholes <- sapply(splits, `[[`, 1L)
  wholes[wholes %in% c("", "-", "+")] <- paste0(wholes[wholes %in% c("", "-", "+")], "0")
  fracs <- sapply(splits, `[`, 2L)
  fracs[is.na(fracs)] <- "0"
  # because string-length is used in our calcs ...
  fracs <- gsub("0+$", "0", fracs)
  whole10 <- strtoi(wholes, base = base)
  frac10 <- strtoi(fracs, base = base) / (base^nchar(fracs))
  whole10 + sign(whole10)*frac10
}

r2evans
  • 141,215
  • 6
  • 77
  • 149
  • `base2float("111101111.010111")=495.3594 <> "495.359375"=from2to10(111101111.010111)` Difference is negligable. `base2float("0.001")=0 <> "0.125"=from2to10(0.001)`. So, `base2float` may be perhaps fine-tuned when printing the result. – Erdogan CEVHER Jun 03 '19 at 04:43
  • 2
    It only yields 0.3594 if your `options("digits")` tells it to. (With `options(digits=8)`, it yields `495.35938` which is closer. In this case, the function is correct, it's the representation on the console that is to blame for the *apparent*, not actual, difference.) – r2evans Jun 03 '19 at 05:44
  • 1
    `base2float("111101111.010111") == (strtoi("111101111", 2) + 1/4 + 1/16 + 1/32 + 1/64)` is true. – r2evans Jun 03 '19 at 05:48
  • I learned to change `options("digits")` to control actual/apparent difference, and `strtoi`, now. Even from your comments, one can learn many things. Many many thanks. – Erdogan CEVHER Jun 03 '19 at 08:41
  • Also, [here] (https://stackoverflow.com/questions/56411025/how-to-convert-a-binary-fraction-number-into-decimal-fraction-number-in-r), a user (martin_joerg) found a solution, that is from numeric to numeric. – Erdogan CEVHER Jun 03 '19 at 08:46
  • Nice find! I like `base2decimal`'s use of exponentiation to resolve the fractional component. – r2evans Jun 03 '19 at 13:59
1
library(cwhmisc) # int, frac
from2to10 <- function(n) {
SignOfNumber <- ""
if (n < 0) {
n <- abs(n)
SignOfNumber <- "-"}

nWhole <- int(n)
nWhole <- as.character(nWhole)

nFraction <- frac(n)
nFraction <- as.character(nFraction)

DecimalWhole   <- sapply(strsplit(nWhole, split=""), function(x) sum(as.numeric(x) * 2^(rev(seq_along(x) - 1))))

if (nFraction == 0) {
DecimalFraction <- ""
paste0(SignOfNumber, DecimalWhole)
} else { # Find decimal fraction part

part3 <- function(x, y, z) { eval(parse(text=(paste(x, y, z,sep="")))) }
y <- as.numeric(strsplit(substr(part3("\"",n,"\""), which(strsplit(part3("\"",n,"\""), "")[[1]]==".") + 1, nchar(part3("\"",n,"\""))),"")[[1]])
DecimalFraction <- sum(y * (0.5^(1:length(y))))
paste0(SignOfNumber, DecimalWhole + DecimalFraction)
}
}

from2to10(10100101) # "165"
from2to10(0) # "0"
from2to10(10100101.01) # "165.25"
from2to10(-10100101) # "-165"
from2to10(-10100101.01) # "-165.25"
from2to10(111101111.010111) # "495.359375"; numeric to string; exact conversion
base2float("111101111.010111") # 495.3594; string to numeric; conversion with rounding. (r2evans)
Erdogan CEVHER
  • 1,788
  • 1
  • 21
  • 40
  • 1
    `paste` does not give a *"base10 number in R"*, perhaps you should restate your expectations – r2evans Jun 03 '19 at 00:54
  • @r2evans You are right. But, when I use `as.numeric(from2to10(111101111.010111)) # [1] 495.3594`, it rounds, and does not find exact value 495.359375. So, I did not put `as.numeric( paste0(...) )` in the function body. If I can find a way to prevent rounding, I will put it in the body of the function. Also, numeric to numeric will be crema in the paste :) – Erdogan CEVHER Jun 03 '19 at 01:02
  • 1
    If you are defining a floating-point number without quotes, then it is `numeric` base 10. I don't know of a way around R FAQ-7.31, which means you will likely (without hackery) often (not always) have differences where you'll find a non-0/1 in the data. Where are you getting these "number" such that (1) you know they are not base-10, but (2) R thinks they are numbers? Perhaps you should go back to that stage and "encourage" R to read them in as strings, not numbers. It's likely better that way. (And then you can use my function :-) – r2evans Jun 03 '19 at 03:48
  • _If you are defining a floating-point number without quotes, then it is numeric base 10_: now, I understand why you go from string to numeric. I didn't think this before. You are 100% right. I'll answer the remaining parts of your comment as well. – Erdogan CEVHER Jun 03 '19 at 03:53
  • Your logic (_Perhaps you should go back to that stage and "encourage" R to read them in as strings, not numbers_) is fully rightful based on the above fact. I understand it lately. I will give the root/source of these "number" in a question. It is a little long. – Erdogan CEVHER Jun 03 '19 at 04:10
  • 1
    I understand, and it's rarely as simple as questions/answers on SO suppose it will be. Often, questioning assumptions can help us unearth simpler fixes, allowing sometimes more elegant/resilient/robust solutions or, lacking that, less problematic hacks. Good luck! – r2evans Jun 03 '19 at 04:17