如何将给定八位字节(字节)的最后n位设置为零?

时间:2014-09-26 21:57:36

标签: lisp common-lisp bit-manipulation

我得到一个八位字节(字节)和一些相关位,我想保留该给定字节的前n个(相关位)并将剩余的位设置为零。

E.g。

前4位相关的数字217将转换为 208

0                    0
0 1 2 3 4 5 6 7      0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+  ==> +-+-+-+-+-+-+-+
1 1 0 1 1 0 0 1      1 1 0 1 0 0 0 0

前8个(或更多)位相关的数字255根本不会改变

0                    0
0 1 2 3 4 5 6 7      0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+  ==> +-+-+-+-+-+-+-+
1 1 1 1 1 1 1 1      1 1 1 1 1 1 1 1

我写了这个函数来解决问题

(defun list-of-bits (integer)
  (let ((bits '()))
    (dotimes (position (integer-length integer) bits)
      (push (ldb (byte 1 position) integer) bits))))

(defun calculate-octet (byte rel-bits)
  (if (< rel-bits 8)
      (parse-integer 
       (FORMAT nil "~{~a~}"
           (replace (list-of-bits byte)
            '(0 0 0 0 0 0 0 0) 
            :start1 rel-bits
            :end1 8
            :start2 0
            :end2 rel-bits))
       :radix 2)
      byte))

但是这个解决方案看起来很糟糕有两个原因

  1. 它在处理之前将整数拆分为一个列表

  2. 它将列表打印成一个再次解析的字符串

  3. 我知道common-lisp提供了直接访问位和字节(ldb,logbitp)的函数,但我无法找到一种方法来将它们用于我的问题。

    有没有办法更有效地解决问题(或至少更优雅)?

4 个答案:

答案 0 :(得分:7)

CL-USER 6 > (mask-field (byte 4 4) 217)
208

另请注意,它不会屏蔽原始数字的字段,但会在屏蔽位字段时返回一个新数字。

答案 1 :(得分:6)

已有三个答案,所以我不妨再添两个:

还没有人建议DPB。对PDP-10的点头总是很好。

(defun calculate-octet (byte bits)
  (dpb 0 (byte bits bits) byte))

再次使用LOGAND

(defun calculate-octet (byte bits)
  (logand byte (ash -1 (- 8 bits))))

答案 2 :(得分:2)

ash可以解决问题:

(defun calculate-octet (byte bits)
  (let ((shift (- 8 bits)))
    (ash (ash byte (- shift)) shift)))

测试:

? (calculate-octet 217 4)
208
? (calculate-octet 255 8)
255

答案 3 :(得分:0)

我认为你需要的是LOGAND。

(defun calculate-octet (byte rel-bits)
  (ecase rel-bits
    (0 0)
    (1 (logand byte #b10000000))
    (2 (logand byte #b11000000))
    (3 (logand byte #b11100000))
    (4 (logand byte #b11110000))
    (5 (logand byte #b11111000))
    (6 (logand byte #b11111100))
    (7 (logand byte #b11111110))
    (8 byte)))