开发者

Converting decimal to binary in R?

What would be the easiest way to convert a number to base 2 (in a string, as for example 5 would be converted to "0000000000000101") in R? There is intToBits, but it returns a vector of strings rather than a string:

>开发者_如何学Python intToBits(12)
 [1] 00 00 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[26] 00 00 00 00 00 00 00

I have tried some other functions, but had no success:

> toString(intToBits(12))
[1] "00, 00, 01, 01, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00"


paste(rev(as.integer(intToBits(12))), collapse="") does the job

paste with the collapse parameter collapses the vector into a string. You have to use rev to get the correct byte order though.

as.integer removes the extra zeros


Note that intToBits() returns a 'raw' vector, not a character vector (strings). Note that my answer is a slight extension of @nico's original answer that removes the leading "0" from each bit:

paste(sapply(strsplit(paste(rev(intToBits(12))),""),`[[`,2),collapse="")
[1] "00000000000000000000000000001100"

To break down the steps, for clarity:

# bit pattern for the 32-bit integer '12'
x <- intToBits(12)
# reverse so smallest bit is first (little endian)
x <- rev(x)
# convert to character
x <- as.character(x)
# Extract only the second element (remove leading "0" from each bit)
x <- sapply(strsplit(x, "", fixed = TRUE), `[`, 2)
# Concatenate all bits into one string
x <- paste(x, collapse = "")
x
# [1] "00000000000000000000000000001100"

Or, as @nico showed, we can use as.integer() as a more concise way to remove the leading zero from each bit.

x <- rev(intToBits(12))
x <- paste(as.integer(x), collapse = "")
# [1] "00000000000000000000000000001100"

Just for copy-paste convenience, here's a function version of the above:

dec2bin <- function(x) paste(as.integer(rev(intToBits(x))), collapse = "")


I think that you can use R.utils package, then the intToBin() function

>library(R.utils)

>intToBin(12)
[1] "1100"

> typeof(intToBin(12))
[1] "character"


intToBits is limited to maximum 2^32, but what if we want to convert 1e10 to binary? Here is function for converting float numbers to binary, assuming as they are big integers stored as numeric.

dec2bin <- function(fnum) {
  bin_vect <- rep(0, 1 + floor(log(fnum, 2)))
  while (fnum >= 2) {
    pow <- floor(log(fnum, 2))
    bin_vect[1 + pow] <- 1
    fnum <- fnum - 2^pow
  } # while
  bin_vect[1] <- fnum %% 2
  paste(rev(bin_vect), collapse = "")
} #dec2bin

This function begins to loose digits after 2^53 = 9.007199e15, but works fine for smaller numbers.

microbenchmark(dec2bin(1e10+111))
# Unit: microseconds
#                 expr     min       lq     mean   median      uq    max neval
# dec2bin(1e+10 + 111) 123.417 125.2335 129.0902 126.0415 126.893 285.64   100
dec2bin(9e15)
# [1] "11111111110010111001111001010111110101000000000000000"
dec2bin(9e15 + 1)
# [1] "11111111110010111001111001010111110101000000000000001"
dec2bin(9.1e15 + 1)
# [1] "100000010101000110011011011011011101001100000000000000"


Have a look at the R.utils package - there you have a function called intToBin...

http://rss.acs.unt.edu/Rdoc/library/R.utils/html/intToBin.html


Oh, but what to do if you have a 64 bit integer as enabled by the bit64 package? Every answer given, other than that of @epwalsh will not operate on the 64 bit integer because the C based internals of R and R.utils do not support it. @epwalsh's solution is great and works in R if you load the bit64 package first, except it (using loops) in R is dog slow (all speeds are relative).

o.dectobin <- function(y) {
  # find the binary sequence corresponding to the decimal number 'y'
  stopifnot(length(y) == 1, mode(y) == 'numeric')
  q1 <- (y / 2) %/% 1
  r <- y - q1 * 2
  res = c(r)
  while (q1 >= 1) {
    q2 <- (q1 / 2) %/% 1
    r <- q1 - q2 * 2
    q1 <- q2
    res = c(r, res)
  }
  return(res)
}

dat <- sort(sample(0:.Machine$integer.max,1000000))
system.time({sapply(dat,o.dectobin)})
#   user  system elapsed 
# 61.255   0.076  61.256 

We can make this better if we byte compile it...

library(compiler)
c.dectobin <- cmpfun(o.dectobin)
system.time({sapply(dat,c.dectobin)})
#   user  system elapsed 
# 38.260   0.010  38.222 

... but it is still pretty slow. We can get substantially faster if we write our own internals in C (which is what I have done here borrowing from @epwalsh's code - I'm not a C programmer, obviously)...

library(Rcpp)
library(inline)
library(compiler)
intToBin64.worker <- cxxfunction( signature(x = "string") , '    
#include <string>
#include <iostream>
#include <sstream>
#include <algorithm>
// Convert the string to an integer
std::stringstream ssin(as<std::string>(x));
long y;
ssin >> y;

// Prep output string
std::stringstream ssout;


// Do some math
int64_t q2;
int64_t q1 = (y / 2) / 1;
int64_t r = y - q1 * 2;
ssout << r;
while (q1 >= 1) {
q2 = (q1 / 2) / 1;
r = q1 - q2 * 2;
q1 = q2;
ssout << r;
}


// Finalize string
//ssout << r;
//ssout << q1;
std::string str = ssout.str();
std::reverse(str.begin(), str.end());
return wrap(str);
', plugin = "Rcpp" )

system.time(sapply(as.character(dat),intToBin64.worker))
#   user  system elapsed 
#  7.166   0.010   7.168 

```


This function will take a decimal number and return the corresponding binary sequence, i.e. a vector of 1's and 0's

dectobin <- function(y) {
  # find the binary sequence corresponding to the decimal number 'y'
  stopifnot(length(y) == 1, mode(y) == 'numeric')
  q1 <- (y / 2) %/% 1
  r <- y - q1 * 2
  res = c(r)
  while (q1 >= 1) {
    q2 <- (q1 / 2) %/% 1
    r <- q1 - q2 * 2
    q1 <- q2
    res = c(r, res)
  }
  return(res)
}


Try »binaryLogic«

library(binaryLogic)

ultimate_question_of_life_the_universe_and_everything <- as.binary(42)

summary(ultimate_question_of_life_the_universe_and_everything)
#>   Signedness  Endianess value<0 Size[bit] Base10
#> 1   unsigned Big-Endian   FALSE         6     42

> as.binary(0:3, n=2)
[[1]]
[1] 0 0

[[2]]
[1] 0 1

[[3]]
[1] 1 0

[[4]]
[1] 1 1


--originally added as an edit to @JoshuaUlrich's answer since it's entirely a corollary of his and @nico's; he suggested I add a separate answer since it introduces a package outside his ken--

Since @JoshuaUlrich's answer is so functional (6 back-to-back functions), I find the pipe (%>%) operator of magrittr/tidyverse makes the following solution more elegant:

library(magrittr)

intToBits(12) %>% rev %>% as.integer %>% paste(collapse = '')
# [1] "00000000000000000000000000001100"

We can also add one final as.integer call to truncate all those leading zeros:

intToBits(12) %>% rev %>% as.integer %>% paste(collapse = '') %>% as.integer
# [1] 1100

(note of course that this again stored as integer, meaning R considers it as 1100 represented in base 10, not 12 represented in base 2)

Note that @ramanudle's (and others', notably @russellpierce, who gives a C++ implementation) approach is often the standard suggested in low-level languages as it's quite an efficient approach (and it works for any number that can be stored in R, i.e, not limited to integer range).

Also worth mentioning that the C implementation of intToBits is remarkably straightforward -- see https://en.wikipedia.org/wiki/Bitwise_operations_in_C for the parts that may be unfamiliar to R-only users


Here's a recursive function that converts a positive integer to any base from 2 to 9. The function works by repeatedly dividing by the base and converting the quotient to the target base, by calling itself. The digits of the answer are the remainders of each division along the way.

convertBase <- function(x, base=2L, g="") {
  if (x < 1) return(g)
  convertBase( x %/% base, base, paste0(x %% base, g) )
}

For example, convertBase(545,6) will first divide 545 by 6, giving 90 remainder 5. So "5" is the rightmost digit, and then the function calls convertBase(90,6,"5") which divides 90 by 6 giving 15 remainder 0. Thus "0" is the next digit (moving left), and the function calls convertBase(15,0,"05") which divides 15 by 6 giving 2 remainder 3, so the next digits (again moving left) are "3" and finally "2", returning "2305". The default base is 2 (binary); for example convertBase(12) gives "1100".

If x is 0 (or negative), the function returns "". If x is not integral, the function won't work. If you need to convert to a base larger than 10, the function as I've presented it won't work but it is not hard to adapt.


decimal.number<-5

i=0

result<-numeric()

while(decimal.number>0){

  remainder<-decimal.number%%2

  result[i]<-remainder

  decimal.number<-decimal.number%/%2

  i<-i+1
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜