rgbds
rgbds copied to clipboard
Implement `opt Q` for fixed-point precision
Fixes #957
Should "certain-precision" literals (which would be interpreted with a given precision regardless of the opt f level) use p or q?
I'm leaning toward q, since it matches the "Qm.n" notation; and unlike #956, there's no %p format spec for them to go with. Also this PR uses opt f not opt p because p is already taken for the pad value; but I'd rather not do 12.34f8 since it's confusable with a hex digit.
(Also maybe this should use -q and opt q instead of f?)
I like the idea of going with q, and in that case, it seems sensible to just use -q for the option too.
Discussion of a problem with this so far: https://discord.com/channels/303217943234215948/661193788802203688/921569990791036928
I think opt Q is ready to review as-is; 12.34q.8 literals can be their own PR. (Even without them, just having opt Q makes it easier to write a fully Q.8 fixed-point project, for instance.)
Edit: ...huh, at some point I implemented q literals already, with tests for them, and just forgot. :P
pret will be able to use this when 0.6.0 is out: the sine_table macro can be simplified from this:
MACRO sine_table
; \1 samples of sin(x) from x=0 to x<0.5 units (pi radians)
DEF x = 0
rept \1
dw (sin(x) + (sin(x) & $ff)) >> 8 ; round up
DEF x += div(0.5, \1) ; a circle has 1.0 units (tau radians)
endr
ENDM
to this, with rgbasm -Q8 and with a workable for loop since two's-complement underflow is no longer a problem:
MACRO sine_table
; \1 samples of sin(x) from x=0 to x<0.5 units (pi radians)
for x, \1
dw sin(x * div(0.5, \1)) ; a circle has 1.0 units (tau radians)
endr
ENDM
My polishedcrystal project also has an opportunity to use opt Q. Most fixed-point math in it is best done with -Q8, just like pret, but one line is not: ld de, div(1.0, 25.4) (1 inch / 25.4 mm = 0.03937 in/mm) expects ld de, $0A14, but with 8.8 fixed-point that would be ld de, $000A. The solution:
pusho
opt Q16
ld de, div(1.0, 25.4) ; 1 in / 25.4 mm = 0.03937 in/mm
popo
(This can't just use q literals, as ld de, div(1.0q16, 25.4q16) would still be doing the division in the default -Q8 context.)
Looks good to me, although the issue with insufficient literal precision remains to be fixed.
This is referring to how fixed-point literals are not currently lexed with enough precision to specify all 65,536 possible values, at least not for extreme values of Q. For instance, at -Q31 (Q1.31 fixed-point), 0.0000000009 is $0000 but 0.0000000010 is $0002, with no literal for $0001. IMO not a blocker for this feature.
Looks good to me, although the issue with insufficient literal precision remains to be fixed.
This is referring to how fixed-point literals are not currently lexed with enough precision to specify all 65,536 possible values, at least not for extreme values of
Q. For instance, at-Q31(Q1.31 fixed-point),0.0000000009is$0000but0.0000000010is$0002, with no literal for$0001. IMO not a blocker for this feature.
Agreed, hence my approving review. (By the way, since only 9 decimal places are lexed, any precision >= 9 can have rounding errors. Of course, 31 maximises them.)