简体   繁体   中英

how to get 64 bit integer in common lisp?

I want to write a bitboard in common lisp, so I need a 64 bit integer. How do I get a 64 bit integer in common lisp? Also, are there any libraries that could help me accomplish this without writing everything from scratch?

You can declare your variables to be of type (signed-byte 64) or (unsigned-byte 64) :

CL-USER> (typexpand '(unsigned-byte 64))
(INTEGER 0 18446744073709551615)
T
CL-USER> (typexpand '(signed-byte 64))
(INTEGER -9223372036854775808 9223372036854775807)
T

It depends upon your implementation if it is actually clever enough to really stuff this in 8 consecutive bytes or if it will use a bignum for this. Appropriate optimize -declarations might help.

Here's a (very simple) example of such type declarations, and handling integers in binary:

(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

Here's a setf-expander definition to get a simple setter for bits in integers, and a corresponding 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))

These can be used like this:

(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

In portable Common Lisp 'Integers' are as large as you like. There is a more efficient subset of integers called 'fixnums'. The exact range of fixnums is implementation depended. But it is typically not the full 64 bit (on a 64bit architecture) which can be used, since most Common Lisp implementations need type tag bits. For the user there is not much of a difference. Fixnums are a subset of integers and one can add two fixnums and get a not-fixnum integer result. The only differences that may be observable is that computation with non-fixnum integers is slower, needs more storage, ... Generally, if you want to do computation with integers, you don't need to declare that you want to calculate with 64bit. You just use Integers and the usual operations for those.

If you want real 64bit large integers (represented in only 64bits, without tags, etc.) and computation with those, you'll leave the portable ANSI CL capabilities. If and how CLISP supports that, is best asked on the CLISP mailing list.

Documentation

Example usage of bit vectors/arrays to implement a 8x8 bit-board (starting with brutally and prematurely optimized code just to show a way to get tight assembler code):

(defun make-bitboard ()
  (make-array '(8 8) :element-type '(mod 2) :initial-element 0))

MAKE-BITBOARD will create a 8x8 bitboard as an array of bits. When using SBCL, this is internally represented as 1 bit per element (so you have 64 bits + array instance overhead). If you ask for optimizations when accessing the board, you'll get fast code.

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

The DECLAIM s are there to allow local inlining requests for GET-BITBOARD .

An example of using 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))

Since there is no SET-BITBOARD yet, an example of using USE-BITBOARD is:

(use-bitboard (make-bitboard))

Disassembling USE-BITBOARD (SBCL again, Linux x64) shows that the compiler inlined GET-BITBOARD :

; 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

Not sure why the compiler put in all those NOP s (leaving space for instrumentation later? alignments?) but if you look at the code at the end it's pretty compact (not as compact as hand-crafted assembler, of course).

Now this is an obvious case of premature optimization. The correct way to start here would be to simply write:

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

... and then use a profiler when running the game code that uses the bit-board to see where the CPU bottlenecks are. SBCL includes a nice statistical profiler .

Starting with the simpler and slower code, with no declarations for speed, is best. Just compare the size of the code - I started with the code with plenty of declarations to make the simple code at the end look even simpler by comparison :-). The advantage here is that you can treat Common Lisp as a scripting/prototyping language when trying out ideas, then squeeze more performance out of the code that the profiler suggests.

The assembly code is obviously not as tight as loading the whole board in one 64 bit register and then accessing individual bits. But if you suddenly decide that you want more than 1 bit per square, it's much easier to change the CL code than to change assembler code (just change the array type everywhere from '(mod 2) to '(mod 16) , for instance).

You want to use bit vectors, which are arbitrary sized arrays of bits, rather than something like a 64 bit integer. The implementation will deal with the internal representations for you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM