prologclpfdprolog-toplevel

SWI-Prolog reporting wrong answer with bitshifts CLPFD


I encountered this in a much larger codebase, but reduced it down to a minimal reproducible example. This is some code for an assembler:

:- use_module(library(clpfd)).

bigconst(X) :- X #=< 0x3FF, X #>= 0.

asm(instruction('ADD', [A]), R) :-
  bigconst(A),
  R #= 0b0000 + (A << 4).
asm(instruction('SUB', [A]), R) :-
  bigconst(A),
  R #= 0b0001 + (A << 4).

It seems to work when assembling:

?- asm(instruction('SUB', [5]), R).
R = 81.

But seems to fail when disassembling:

?- asm(I, 81).
I = instruction('ADD', [_42146]),
_42146 in 0..1023,
81#=_42146<<4 .

Is this a bug in my program or a bug in Prolog? How would I fix this?


Solution

  • When I found the answer it was LOL. I have used many odd patterns to solve a problem but this was one I have never used before. Once I saw it work I knew I had a shinny new tool for tool box.

    With CLP(FD) problems they typically can work both ways and is what you wanted. The first problem you have is that you have bigconst(A) which is acting like guard statement. So just toss that out.

    Then next thing is that R #= 0b0000 + (A << 4) works as expected but suffers from a problem, it doesn't work as desired both ways,

    ?- X #= 4 << 4.
    X = 64.
    
    ?- 64 #= X << 4.
    64#=X<<4.
    

    Likewise the reverse

    B #= A >> 4.
    

    also works as expected and also suffers the same problem.

    ?- X #= 64 >> 4.
    X = 4.
    
    ?- 4 #= X >> 4.
    4#=X>>4.
    

    So I tried adding a few constraints using in/2 and that didn't work, then realized I already had all the constraints needed and they worked.

    asm(instruction('ADD', [A]), R) :-
        R #= 0b0000 + (A << 4),
        A #= (R - 0b0000) >> 4.
    

    Example usage

    ?- asm(instruction('ADD', [5]), R).
    R = 80.
    
    ?- asm(I,80).
    I = instruction('ADD', [5]).
    

    To show that it was not a one hit wonder.

    asm(instruction('SUB', [A]), R) :-
        R #= 0b0001 + (A << 4),
        A #= (R - 0b0001) >> 4.
    

    Example usage

    ?- asm(instruction('SUB', [5]), R).
    R = 81.
    
    ?- asm(I,81).
    I = instruction('SUB', [5]).