【问题标题】:Converting decimal to binary in R?在R中将十进制转换为二进制?
【发布时间】:2011-07-07 16:59:51
【问题描述】:

在 R 中将数字转换为基数 2(在字符串中,例如 5 将转换为 "0000000000000101")的最简单方法是什么?有intToBits,但它返回的是字符串向量而不是字符串:

> 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

我尝试了其他一些功能,但没有成功:

> 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"

【问题讨论】:

  • intToBits not 返回字符串向量。它返回一个原始向量。请注意,向量有 32 个元素。这是每个位的一个元素(因为 R 使用 32 位整数)。我想不出将数字表示为位字面串有用的情况......你想做什么?
  • 我正在研究一些密码分析的例子,很高兴能够将密钥显示为位序列、“011010110”等。
  • @DWin:它实际上在 Debian 中被列为“GNU R 统计计算和图形系统”,并且项目页面说它是一个 GNU 项目,这就是我称之为 GNU R 的原因。不是我对这些东西很挑剔——我习惯说“GNU R”来帮助消除歧义(在 Google 上搜索“R”并没有什么用处)。
  • 看到它被称为 GNU R 让 R Core 很恼火。由于他们是作者,我认为他们拥有最终决定权。并且在 GNU R 上搜索会错过网络上的大部分内容。使用“r-project”作为术语或使用 RSiteSearch() 或 rseek 作为搜索引擎。有些人报告说,将“r:language”作为 Google 术语取得了成功。
  • @42- 运气不好。如果它惹恼了核心作者,他们不应该将其列为 GNU 项目。然而他们做到了,并继续在官方网站上这样做。

标签: r


【解决方案1】:

paste(rev(as.integer(intToBits(12))), collapse="") 完成这项工作

pastecollapse 参数将向量折叠成一个字符串。不过,您必须使用 rev 才能获得正确的字节顺序。

as.integer 删除多余的零

【讨论】:

    【解决方案2】:

    请注意,intToBits() 返回一个“原始”向量,而不是字符向量(字符串)。请注意,我的答案是对@nico's original answer 的轻微扩展,它从每个位中删除了前导“0”:

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

    为了清楚起见,分解步骤:

    # 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"
    

    或者,作为@nico showed,我们可以使用as.integer() 作为更简洁的方式来删除每个位的前导零。

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

    为了方便复制粘贴,这里是上面的函数版本:

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

    【讨论】:

    • @bubakazouba:在本例中,12 是一个 32 位整数。为什么你认为它有太多位?要解决什么问题?
    • 对不起,我的意思是我需要的东西很多,我的意思不是“修复”,因为有问题需要修复。我只是说有没有一种简单的方法来改变位数?
    • @bubakazouba:简而言之,没有。 Base R 只有 32 位整数。如果您知道该数字可以用较少的位数表示(例如一个字节或短字节),您可以使用substr 仅提取最右边的 X 位。但是你真的应该使用readBinwriteBin 来处理二进制数据。
    【解决方案3】:

    我认为你可以使用 R.utils 包,然后使用 intToBin() 函数

    >library(R.utils)
    
    >intToBin(12)
    [1] "1100"
    
    > typeof(intToBin(12))
    [1] "character"
    

    【讨论】:

      【解决方案4】:

      intToBits 被限制为最大 2^32,但是如果我们想将 1e10 转换为二进制呢?这是将浮点数转换为二进制的函数,假设它们是存储为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
      

      此函数在 2^53 = 9.007199e15 之后开始松动数字,但适用于较小的数字。

      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"
      

      【讨论】:

      • 我不需要转换这么大的数字,但无论如何都是很好的答案! +1
      • 我遇到了一个问题,我需要使用大数字进行操作,在 stackoverflow 上搜索解决方案后,我终于编写了自己的代码 :)
      • 我赞成这个答案,因为它涵盖了存储为数字的大整数的情况。我很高兴看到 inscaven 的回答也将涵盖小数情况:dec2bin(0.3) # Error in rep(0, 1 + floor(log(fnum, 2))) : invalid 'times' argument. 另外,请注意dec2bin(0) # Error in rep(0, 1 + floor(log(fnum, 2))) : invalid 'times' argument。因此,必须妥善处理案例0。
      • @inscaven,你是否也为大的“bin”字符串实现了逆 bin2dec?
      • @mshaffer 如果你有一个二进制字符串 bs 你可以使用这个单行代码 sum(2^(nchar(bs) - stringi::stri_locate_all(bs, fixed = "1")[[1]][,1])) 请记住它可以正确处理长度不超过 53 个字符的二进制字符串
      【解决方案5】:

      看看 R.utils 包 - 你有一个名为 intToBin 的函数...

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

      【讨论】:

        【解决方案6】:

        哦,但是如果您有一个由 bit64 包启用的 64 位整数,该怎么办?给出的每个答案,除了@epwalsh 的答案,都不会在 64 位整数上运行,因为基于 C 的 R 和 R.utils 内部不支持它。 @epwalsh 的解决方案很棒,如果你先加载 bit64 包,它可以在 R 中工作,除了它(使用循环)在 R 中很慢(所有速度都是相对的)。

        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 
        

        如果我们对它进行字节编译,我们可以让它变得更好......

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

        ...但它仍然很慢。如果我们用 C 编写自己的内部代码(这是我在这里借用 @epwalsh 的代码所做的——显然我不是 C 程序员),我们可以变得更快……

        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 
        

        ```

        【讨论】:

        • ...我现在注意到这完全是荒谬的,因为 bit64 有一个 as.bitstring 函数,它的速度是我的 Rcpp 函数的两倍...但是我将把它留在这里作为愚蠢的纪念碑并作为一个潜在的提醒,提醒您如何从 integer64 桥接到 C++ 并返回......但如果您需要更有效的方法来做到这一点,请务必查看 bit64 源代码。
        • 你的“愚蠢的纪念碑”评论让我想到了:despair.com/products/mistakes
        • 我想知道是否只是重新调整内部 intToBits 以处理更广泛的输入会不会很好? github.com/wch/r-source/blob/…
        • @MichaelChirico IIRC bit64 在后台以两个双精度实现 64 位整数。因此,如果仅通过更改向量和循环边界就可以顺利进行,我会感到有些惊讶。另外......奇怪的是,三年后看到 cmets 在这个排名较低的答案上。 =)
        • bit64 将 integer64 实现为一个双精度 - 一个 REALSXP。 double 是 64 位,64 位整数也是如此。相同数量的内存,但内容的表示方式不同。我的评论是由于在此页面上解决@MichaelChirico 对我的回答的评论/编辑。我碰巧读到了你的评论,这让我微笑并想到了那个链接。
        【解决方案7】:

        这个函数将取一个十进制数并返回对应的二进制序列,即1和0的向量

        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)
        }
        

        【讨论】:

        • 我觉得写y %/% 2比较好
        【解决方案8】:

        试试 »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
        

        【讨论】:

          【解决方案9】:

          --最初添加为对@JoshuaUlrich 答案的编辑,因为这完全是他和@nico 的推论;他建议我添加一个单独的答案,因为它在他的范围之外引入了一个包--

          由于@JoshuaUlrich 的回答非常实用(6 个背靠背功能),我发现magrittr/tidyverse 的管道(%&gt;%)运算符使以下解决方案更加优雅:

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

          我们还可以添加最后一个 as.integer 调用来截断所有前导零:

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

          (当然请注意,这再次存储为integer,这意味着 R 认为它是以 10 为基数表示的 1100,而不是以 2 为基数的 12)

          请注意,@ramanudle(和其他人,尤其是提供 C++ 实现的 @russellpierce)方法通常是低级语言中建议的标准,因为它是一种非常有效的方法(它适用于任何可以存储的数字在 R 中,即不限于integer 范围)。

          还值得一提的是,C implementation of intToBits 非常简单 - 请参阅 https://en.wikipedia.org/wiki/Bitwise_operations_in_C 了解仅 R 的用户可能不熟悉的部分

          【讨论】:

            【解决方案10】:
            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
            }
            

            【讨论】:

            • 虽然此代码可以回答问题,但提供有关 如何为什么 解决问题的附加上下文将提高​​答案的长期价值。
            猜你喜欢
            • 2012-06-26
            • 1970-01-01
            • 2013-10-30
            • 2017-10-15
            • 1970-01-01
            • 2015-06-02
            • 2011-07-09
            • 2016-03-16
            • 2012-12-26
            相关资源
            最近更新 更多