如何在公共lisp中获取64位整数?

如何在公共lisp中获取64位整数?,lisp,common-lisp,bit,clisp,Lisp,Common Lisp,Bit,Clisp,我想用common lisp编写一个位板,所以我需要一个64位整数。如何在common lisp中获取64位整数?还有,有没有什么库可以帮助我在不从头开始编写的情况下完成这项工作 您希望使用位向量,它是任意大小的位数组,而不是类似64位整数的东西。实现将为您处理内部表示。您可以将变量声明为(有符号字节64)或(无符号字节64): 这取决于您的实现,它是否真的足够聪明,可以在8个连续字节中填充,或者它是否将使用bignum。适当的优化-声明可能会有所帮助 下面是一个(非常简单的)此类类型声明和二进

我想用common lisp编写一个位板,所以我需要一个64位整数。如何在common lisp中获取64位整数?还有,有没有什么库可以帮助我在不从头开始编写的情况下完成这项工作

您希望使用位向量,它是任意大小的位数组,而不是类似64位整数的东西。实现将为您处理内部表示。

您可以将变量声明为
(有符号字节64)
(无符号字节64)

这取决于您的实现,它是否真的足够聪明,可以在8个连续字节中填充,或者它是否将使用bignum。适当的
优化
-声明可能会有所帮助

下面是一个(非常简单的)此类类型声明和二进制处理整数的示例:

(let* ((x #b01)
       (y #b10)
       (z (logior x y)))
  (declare ((signed-byte 64) x y z))
  (format t "~a~%" (logbitp 1 x))
  (format t "~a~%" (logbitp 1 (logior x (ash 1 1))))
  (format t "~b~%" z))

Output:
NIL
T
11
下面是一个setf扩展器定义,用于获取整数位的简单setter和相应的getter:

(define-setf-expander logbit (index place &environment env)
  (multiple-value-bind (temps vals stores store-form access-form)
      (get-setf-expansion place env)
    (let ((i (gensym))
          (store (gensym))
          (stemp (first stores)))
      (values `(,i ,@temps)
              `(,index ,@vals)
              `(,store)
              `(let ((,stemp (dpb ,store (byte 1 ,i) ,access-form))
                     ,@(cdr stores))
                 ,store-form
                 ,store)
              `(logbit ,i ,access-form)))))

(defun logbit (index integer)
  (ldb (byte 1 index) integer))
它们可以这样使用:

(let ((x 1))
  (setf (logbit 3 x) 1)
  x)
==> 9
(let ((x 9))
  (setf (logbit 3 x) 0)
  x)
==> 1

(logbit 3 1)
==> 0
(logbit 3 9)
==> 1

在portable Common Lisp中,“整数”可以任意大。有一个更有效的整数子集称为“fixnums”。FIXNUM的确切范围取决于实现。但通常不能使用完整的64位(在64位体系结构上),因为大多数常见的Lisp实现都需要类型标记位。对于用户来说,没有太大区别。fixnum是整数的子集,可以添加两个fixnum并得到一个非fixnum整数结果。唯一可以观察到的区别是,使用非固定整数的计算速度较慢,需要更多存储。。。通常,如果要使用整数进行计算,则不需要声明要使用64位进行计算。您只需使用整数和对其进行的常规操作

如果您想要真正的64位大整数(仅用64位表示,不带标记等)并使用这些整数进行计算,您将保留可移植的ANSI CL功能。最好在CLISP邮件列表中询问CLISP是否以及如何支持该功能

文件

  • 类型
  • 类型

使用位向量/阵列实现8x8位电路板的示例 (从残酷和过早优化的代码开始,只是为了显示 获得紧凑汇编代码的方法):

MAKE-BITBOARD
将创建8x8位板作为位阵列。什么时候 使用SBCL,这在内部表示为每个元素1位(因此 您有64位+阵列实例开销)。如果你要求 优化访问电路板时,您将获得快速代码

(declaim (inline get-bitboard))
(defun get-bitboard (bit-board x y)
       (declare (optimize speed (safety 0) (debug 0))
                (type (simple-array (mod 2) (8 8)) bit-board)
                (type fixnum x y))
       (aref bit-board x y))
(declaim (notinline get-bitboard))
DECLAIM
s允许本地 对于
GET-BITBOARD

使用
GET-BITBOARD
的示例:

(defun use-bitboard (bit-board)
  (declare (optimize speed (safety 0) (debug 0))
           (type (simple-array (mod 2) (8 8)) bit-board)
           (inline get-bitboard))
  (let ((sum 0))
    (declare (type fixnum sum))
    (dotimes (i 8)
      (declare (type fixnum i))
      (dotimes (j 8)
        (declare (type fixnum j))
        (incf sum (the (mod 2) (get-bitboard bit-board i j)))))
    sum))
; disassembly for USE-BITBOARD
; 030F96A2:       31F6             XOR ESI, ESI               ; no-arg-parsing entry point
;      6A4:       31D2             XOR EDX, EDX
;      6A6:       EB54             JMP L3
;      6A8:       90               NOP
;      6A9:       90               NOP
;      6AA:       90               NOP
;      6AB:       90               NOP
;      6AC:       90               NOP
;      6AD:       90               NOP
;      6AE:       90               NOP
;      6AF:       90               NOP
;      6B0: L0:   31DB             XOR EBX, EBX
;      6B2:       EB3E             JMP L2
;      6B4:       90               NOP
;      6B5:       90               NOP
;      6B6:       90               NOP
;      6B7:       90               NOP
;      6B8:       90               NOP
;      6B9:       90               NOP
;      6BA:       90               NOP
;      6BB:       90               NOP
;      6BC:       90               NOP
;      6BD:       90               NOP
;      6BE:       90               NOP
;      6BF:       90               NOP
;      6C0: L1:   488D04D500000000 LEA RAX, [RDX*8]
;      6C8:       4801D8           ADD RAX, RBX
;      6CB:       4C8B4711         MOV R8, [RDI+17]
;      6CF:       48D1F8           SAR RAX, 1
;      6D2:       488BC8           MOV RCX, RAX
;      6D5:       48C1E906         SHR RCX, 6
;      6D9:       4D8B44C801       MOV R8, [R8+RCX*8+1]
;      6DE:       488BC8           MOV RCX, RAX
;      6E1:       49D3E8           SHR R8, CL
;      6E4:       4983E001         AND R8, 1
;      6E8:       49D1E0           SHL R8, 1
;      6EB:       4C01C6           ADD RSI, R8
;      6EE:       4883C302         ADD RBX, 2
;      6F2: L2:   4883FB10         CMP RBX, 16
;      6F6:       7CC8             JL L1
;      6F8:       4883C202         ADD RDX, 2
;      6FC: L3:   4883FA10         CMP RDX, 16
;      700:       7CAE             JL L0
;      702:       488BD6           MOV RDX, RSI
;      705:       488BE5           MOV RSP, RBP
;      708:       F8               CLC
;      709:       5D               POP RBP
;      70A:       C3               RET
由于还没有
SET-BITBOARD
,使用
USE-BITBOARD
的示例如下:

(use-bitboard (make-bitboard))
反汇编
USE-BITBOARD
(同样是SBCL,Linux x64)显示 编译器内联
GET-BITBOARD

(defun use-bitboard (bit-board)
  (declare (optimize speed (safety 0) (debug 0))
           (type (simple-array (mod 2) (8 8)) bit-board)
           (inline get-bitboard))
  (let ((sum 0))
    (declare (type fixnum sum))
    (dotimes (i 8)
      (declare (type fixnum i))
      (dotimes (j 8)
        (declare (type fixnum j))
        (incf sum (the (mod 2) (get-bitboard bit-board i j)))))
    sum))
; disassembly for USE-BITBOARD
; 030F96A2:       31F6             XOR ESI, ESI               ; no-arg-parsing entry point
;      6A4:       31D2             XOR EDX, EDX
;      6A6:       EB54             JMP L3
;      6A8:       90               NOP
;      6A9:       90               NOP
;      6AA:       90               NOP
;      6AB:       90               NOP
;      6AC:       90               NOP
;      6AD:       90               NOP
;      6AE:       90               NOP
;      6AF:       90               NOP
;      6B0: L0:   31DB             XOR EBX, EBX
;      6B2:       EB3E             JMP L2
;      6B4:       90               NOP
;      6B5:       90               NOP
;      6B6:       90               NOP
;      6B7:       90               NOP
;      6B8:       90               NOP
;      6B9:       90               NOP
;      6BA:       90               NOP
;      6BB:       90               NOP
;      6BC:       90               NOP
;      6BD:       90               NOP
;      6BE:       90               NOP
;      6BF:       90               NOP
;      6C0: L1:   488D04D500000000 LEA RAX, [RDX*8]
;      6C8:       4801D8           ADD RAX, RBX
;      6CB:       4C8B4711         MOV R8, [RDI+17]
;      6CF:       48D1F8           SAR RAX, 1
;      6D2:       488BC8           MOV RCX, RAX
;      6D5:       48C1E906         SHR RCX, 6
;      6D9:       4D8B44C801       MOV R8, [R8+RCX*8+1]
;      6DE:       488BC8           MOV RCX, RAX
;      6E1:       49D3E8           SHR R8, CL
;      6E4:       4983E001         AND R8, 1
;      6E8:       49D1E0           SHL R8, 1
;      6EB:       4C01C6           ADD RSI, R8
;      6EE:       4883C302         ADD RBX, 2
;      6F2: L2:   4883FB10         CMP RBX, 16
;      6F6:       7CC8             JL L1
;      6F8:       4883C202         ADD RDX, 2
;      6FC: L3:   4883FA10         CMP RDX, 16
;      700:       7CAE             JL L0
;      702:       488BD6           MOV RDX, RSI
;      705:       488BE5           MOV RSP, RBP
;      708:       F8               CLC
;      709:       5D               POP RBP
;      70A:       C3               RET
不确定编译器为什么要放入所有这些
NOP
s(为 稍后插入?对齐?)但是如果您在 最后,它非常紧凑(当然,没有手工汇编程序那么紧凑) 当然)

现在这是一个明显的过早优化的例子。正确的方法 从这里开始,只需写下:

(defun get-bitboard (bit-board x y)
  (aref bit-board x y))

(defun use-bitboard (bit-board)
  (let ((sum 0))
    (dotimes (i 8)
      (dotimes (j 8)
        (incf sum (get-bitboard bit-board i j))))
    sum))
。。。然后在运行使用 查看CPU瓶颈所在的位板。SBCL包括一个

从更简单、更慢的代码开始,不需要声明 速度,是最好的。只是比较一下代码的大小——我从 使用大量声明编写代码,以在最后生成简单的代码 相比之下,看起来更简单:-)。这里的优点是你 尝试时可以将Common Lisp视为脚本/原型语言 拿出想法,然后从 剖析者建议

汇编代码显然不像加载整个电路板那样紧凑 一个64位寄存器,然后访问单个位。但是如果你 突然决定你想要每平方米超过1比特,这太多了 更改CL代码比更改汇编代码更容易(只需 将数组类型从
'(mod 2)
更改为
'(mod 16)
,以便
实例)。

您使用的是哪种Lisp实现?哪种CL实现?尝试在REPL中评估以下两种形式:
(lisp实现类型)
(lisp实现版本)
。使用CLISP,64位内核上最大的fixnum是(2^48)-1,这意味着每个32位字有六到七位用作标记。实现说明中的即时对象表示讨论证实了这一点:另外,请参见:处理位向量的速度是否与处理64位整数的速度一样快?还有,为什么我不能在lisp中访问64位整数?是因为它取决于实现吗?你能再详细一点吗?我使用的是common lisp 2.48,当我键入(带符号的字节64)时,返回了一个错误
(带符号的字节64)
是一个类型说明符,而不是一个函数。它只能在类型说明符合法的情况下使用。我在回答中添加了一个示例。或者,设置位的另一种方法是
(setf(ldb(byte 1 pos)int)1)
,如本例所示:
(let((x 0))(setf(ldb(byte 1 2)x)1)x)
=>
2
。公共Lisp中的类型声明是对编译器的提示。公共Lisp整数总是无界的,但大整数将以低效的格式存储,并带有指针(bignums)。在64位系统上,有效的原始整数(fixNum)通常小于64位,因为有些位用于类型标记。编译器可能会使用类型声明来去除类型标记,并使用完整的64位原始整数。问题是你对如何做这件事有一个先入为主的概念,它应该如何表现。然而,Mirons示例展示了如何操作位板