I can't stop thinking that there is something odd (pun intended) about your example.
First the fact that your box is 81 by 81. But I also noticed that you specifically place it off-center
That is not quite the center of an 81 by 81 box. The correct center would be (40.5, 40,5). I tried that in your example and that actually works. The box follows the guides without gaps.
Anyway, I can understand why you're puzzled by it. If for example the box (still 81 by 81) is located at (41, 41) its left edge would be at (41-40.5)=0.5. Assuming rounding down we get 0. For the right edge we add the width (0+81)=81. That fits what you see on the screen when x or y => 0.
Lets put the box at (1, 41) (With your move logic we always move in increments of 20). Now the left edge will be at (1-40.5)=-39.5. Rounded down is -40. Right edge would then be (-40+81)=41. This does not match what you see.
However! I am pretty sure the sprite API is implemented in C. And maybe the API developers simply casts the floats to ints before blitting to the screen.
For the x => 0 values before: left edge is at 0.5. Cast to int is 0. Right edge is at 0+81=81. No difference. Everything checks out.
For x < 0: Left edge is at -39.5. Cast to int is -39. Right edge is at -39+81=42. That is precisely what we see.
So I think what you see on screen is an artifact of casting float to int in C. Whether it's a bug or not, I'm not so sure. As mentioned you can fix it by placing the box at it's actual center (40.5, 40.5)