How do you write the type conversion?

Asked 1 years ago, Updated 1 years ago, 75 views

I'm thinking of a code that casts a number to 16 bits to determine positive or negative.

(short)n

How do I write in lisp?

common-lisp

2022-09-30 20:15

3 Answers

This is a supplement to BLUEPIXY's answer.

A can declare a variable to be an integer in a specific range.For example:

 (defun(foox)
  (declare(type(inter-3276832767)x))
  ...)

This allows the compiler to code x as an integer between -32768 and 32767 in ... (although it does not necessarily use a 2-complement expression for a negative number, so it is not guaranteed to fit into 16 bits).

However, the programmer is responsible for passing the correct range of values to foo with the above code.The compiler doesn't convert on its own. It's up to the processing system to do things like (foo1000000).

There can be many interpretations of what to do when the original value is over 16 bits (treat as modulo2^16 or throw an exception or clip).Therefore, it is necessary for the programmer to decide and write on his own.Type declarations mainly serve as tips for compilers to produce good code.(For example, type check is omitted when entering or leaving a 16-bit integer array.)

If you're not writing that kind of performance-critical code, you don't have to worry about "integer type variations" from programmers because numerical conversions are implicit.If it's a positive or negative decision, it's OK to just look up the sign as a number like the one already given.

The following is a little off the beaten track.If you only want to look at the code as a number, you can think of the following code:I wanted to speed it up, so I added the detail optimize.

 (defun sign1(x)
  (declare(optimize(safety0)(debug0)(speed3)))
  (cond((<x0)-1)
        ((>x0)1)
        (t0)))

If you compile this in SBCL and reversely assemble it:

; DISASSEMBLY FOR SIGN 1
; 02AC52C2:48895DF8MOV [RBP-8], RBX; NO-ARG-PARSING ENTRY POINT
;      2C6: 31FF XOREDI, EDI
;      2C8:488BD3MOV RDX, RBX
;      2CB: 488D0C2530040020LEARCX, [#X20000430]; GENERIC-<
;      2D3—FFD1 CALL RCX
;      2D5:488B5DF8MOV RBX, [RBP-8]
;      2D9—7C24JL1
;      2DB: 31FF XOREDI, EDI
;      2DD: 488BD3 MOV RDX, RBX
;      2E0:488D0C2566040020LEARCX, [#X20000466];GENERIC->
;      2E8—FFD1 CALL RCX
;      2EA: BA00000000 MOV EDX, 0
;      2EF:41BB0200000000MOV R11D, 2
;      2F5:490F4FD3 CMOVNLE RDX, R11
;      2F9: L0:488BE5MOV RSP, RBP
;      2FC: F8CLC
;      2FD—5D POP RBP
;      2FE: C3 RET
;      2FF: L1:48C7C2FEFFFFF MOV RDX,-2
;      306—EBF1 JMP L0

You call out external functions such as GENERIC-< for size comparison.Because I don't know what will come across the argument, I have to be able to respond to any value.

If you declare that x is fixnum:

 (defun sign 2(x)
  (declare(optimize(safety0)(debug0)(speed3))
           (fixnum x))
  (cond((<x0)-1)
        ((>x0)1)
        (t0)))

This is what happens (declared x as the range -32768 to 32767, but the code was the same):

;disassembly for SIGN2
; 02B3F91F:4883FA00 CMP RDX, 0; no-arg-parsing entry point
;       23:7C19JL1
;       25:4883FA00CMP RDX, 0
;       29—BA00000000 MOV EDX, 0
;       2E:41BB0200000000MOV R11D, 2
;       34:490F4FD3 CMOVNLE RDX, R11
;       38:L0:488BE5MOV RSP, RBP
;       3B: F8CLC
;       3C—5D POP RBP
;       3D: C3 RET
;       3E: L1:48C7C2FEFFFFFMOV RDX,-2
;       45—EBF1 JMP L0

A comparison of x was made in the machine instructions, but the code for the conversion is nowhere to be found.The compiler expects the programmer to pass only fixnum correctly.

If the calling side of sign2 cannot guarantee it, it is necessary to check on the sign function side.For example, if you want to make an error outside the fixnum range:

 (defun sign 3(x)
  (unless (sb-int:fixnump x)
    (error "out of range!"))
  (locally(declare(optimize(safety0)(debug0)(speed3))
           (fixnum x))
    (cond((<x0)-1)
          ((>x0)1)
          (t 0)))

The code will be larger, but if you pass fixnum, you just need to go through mov,cmp and jne.

;disassembly for SIGN3
; 02C3DB28:40F6C601 TEST SIL, 1; no-arg-parsing entry point
;       2C: 752E JNE L2
;       2E: 488BD6MOV RDX, RSI
;       31:4883FA00CMP RDX, 0
;       35—7D0D JNLL1
;       37:48C7C2FEFFFFFMOV RDX,-2
;       3E: L0:488BE5MOV RSP, RBP
;       41—F8CLC
;       42—5D POP RBP
;       43—C3 RET
;       44:L1:488BD6MOV RDX,RSI
;       47:4883FA00CMP RDX, 0
;       4B: BA00000000 MOV EDX, 0
;       50:41BB0200000000MOV R11D, 2
;       56:490F4FD3 CMOVNLE RDX, R11
;       5A—EBE2JMP L0
;       5C: L2:488975F8MOV [RBP-8], RSI
;       60:488D5C24F0LEARBX, [RSP-16]
;       65:4883EC18 SUB RSP, 24
;       69:488B1560FFFFFF MOV RDX, [RIP-160]; "out of range!"
;       70:488B0561FFFFFF MOV RAX, [RIP-159]; #<FDEFINITION object for ERROR>
;       77—B902000000 MOVECX, 2
;       7C: 48892B MOV [RBX], RBP
;       7F: 488BEB MOV RBP, RBX
;       82—FF5009 CALL QWORD PTR [RAX+9]
;       85:90 NOP
;       86:488B75F8MOVRSI, [RBP-8]
;       8A: CC0A BREAK 10; error trap
;       8C:02 BYTE#X02
;       8D—18 BYTE#X18;INVALID-ARG-COUNT-ERROR
;       8E:54 BYTE#X54;RCX


2022-09-30 20:15

I'm sorry if I'm wrong because I don't know much about it.
Common Lisp does not have a 16-bit integer in the first place.
(Integer type switches naturally)
FIXNUM might be like that, but
Translation functions such as coerce cannot be converted unless the is a number in the FIXNUM range (conversion fails).
So if you want to make a positive or negative decision,
Simply

 (minusp n); negative T
(pluspn); positive number T
(>=n 0); T when greater than or equal to 0

as shown in


2022-09-30 20:15

If you do not consider casts, you can use a dedicated function, signum, to make positive or negative decisions.

http://l1sp.org/cl/signum

(signum-12341234)
;=>-1

(signum 0)
;=>0

(signum 12341234)
;=>1

Regarding the cast, if it's for efficiency, as Shiro and BLUEPIXY have already answered, fixnum (about 60 bits for 64-bit processing systems)There is little need to do so.

If you want to imitate the cast's behavior in C, etc., you will have to make your own work.

 (defun signum-short(x)
  (cond((logbitp15x)-1)
        ((zerop(ldb(byte160)x))0)
        (T1))


(signum-short1)
<=>1


(signum-short-1)
;=>-1


(signum-short 32767)
<=>1


(signum-short-32768)
;=>-1


(signum-short 32768)
;=>-1


(signum-short 0)
<=>0


(signum-short 65536)
<=>0


(signum-short-65536)
<=>0

Of course, the same behavior does not mean that the same code as C appears when optimized.

If you just want to mask with 16 bits, you can write as follows:

 (defun mask-16(int)
  (cond(logbitp15int)
         (dpbint(byte160)-1)
        (t
         (ldb(byte160)int)))


(mask-160)
<=>0

(mask-1665536)
<=>0

(mask-16# b111111111111111111111111111111111)
;=>-1

(mask-16-32768)
;=>-32768

(mask-1632768)
;=>-32768


2022-09-30 20:15

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.