Fixed-point functions can take specific precision (#1086)

This commit is contained in:
Rangi
2022-10-02 10:56:08 -04:00
committed by GitHub
parent 8553b61a94
commit 3c049983f1
7 changed files with 143 additions and 94 deletions

View File

@@ -13,22 +13,22 @@
extern uint8_t fixPrecision; extern uint8_t fixPrecision;
uint8_t fix_Precision(void);
double fix_PrecisionFactor(void); double fix_PrecisionFactor(void);
void fix_Print(int32_t i); int32_t fix_Sin(int32_t i, int32_t q);
int32_t fix_Sin(int32_t i); int32_t fix_Cos(int32_t i, int32_t q);
int32_t fix_Cos(int32_t i); int32_t fix_Tan(int32_t i, int32_t q);
int32_t fix_Tan(int32_t i); int32_t fix_ASin(int32_t i, int32_t q);
int32_t fix_ASin(int32_t i); int32_t fix_ACos(int32_t i, int32_t q);
int32_t fix_ACos(int32_t i); int32_t fix_ATan(int32_t i, int32_t q);
int32_t fix_ATan(int32_t i); int32_t fix_ATan2(int32_t i, int32_t j, int32_t q);
int32_t fix_ATan2(int32_t i, int32_t j); int32_t fix_Mul(int32_t i, int32_t j, int32_t q);
int32_t fix_Mul(int32_t i, int32_t j); int32_t fix_Mod(int32_t i, int32_t j, int32_t q);
int32_t fix_Mod(int32_t i, int32_t j); int32_t fix_Div(int32_t i, int32_t j, int32_t q);
int32_t fix_Div(int32_t i, int32_t j); int32_t fix_Pow(int32_t i, int32_t j, int32_t q);
int32_t fix_Pow(int32_t i, int32_t j); int32_t fix_Log(int32_t i, int32_t j, int32_t q);
int32_t fix_Log(int32_t i, int32_t j); int32_t fix_Round(int32_t i, int32_t q);
int32_t fix_Round(int32_t i); int32_t fix_Ceil(int32_t i, int32_t q);
int32_t fix_Ceil(int32_t i); int32_t fix_Floor(int32_t i, int32_t q);
int32_t fix_Floor(int32_t i);
#endif // RGBDS_ASM_FIXPOINT_H #endif // RGBDS_ASM_FIXPOINT_H

View File

@@ -346,6 +346,15 @@ delim $$
delim off delim off
.EN .EN
.Pp .Pp
All of these fixed-point functions can take an optional final argument, which is the precision to use.
For example,
.Ql MUL(6.0q8, 7.0q8, 8)
will evaluate to
.Ql 42.0q8
no matter what value is set as the current
.Cm Q
option.
.Pp
The trigonometry functions ( The trigonometry functions (
.Ic SIN , .Ic SIN ,
.Ic COS , .Ic COS ,

View File

@@ -11,7 +11,6 @@
#include <inttypes.h> #include <inttypes.h>
#include <math.h> #include <math.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include "asm/fixpoint.h" #include "asm/fixpoint.h"
#include "asm/symbol.h" #include "asm/symbol.h"
@@ -21,8 +20,8 @@
#define M_PI 3.14159265358979323846 #define M_PI 3.14159265358979323846
#endif #endif
#define fix2double(i) ((double)((i) / fix_PrecisionFactor())) #define fix2double(i, q) ((double)((i) / pow(2.0, q)))
#define double2fix(d) ((int32_t)round((d) * fix_PrecisionFactor())) #define double2fix(d, q) ((int32_t)round((d) * pow(2.0, q)))
// 2*pi radians == 1 turn // 2*pi radians == 1 turn
#define turn2rad(f) ((f) * (M_PI * 2)) #define turn2rad(f) ((f) * (M_PI * 2))
@@ -30,96 +29,87 @@
uint8_t fixPrecision; uint8_t fixPrecision;
uint8_t fix_Precision(void)
{
return fixPrecision;
}
double fix_PrecisionFactor(void) double fix_PrecisionFactor(void)
{ {
return pow(2.0, fixPrecision); return pow(2.0, fixPrecision);
} }
void fix_Print(int32_t i) int32_t fix_Sin(int32_t i, int32_t q)
{ {
uint32_t u = i; return double2fix(sin(turn2rad(fix2double(i, q))), q);
char const *sign = "";
if (i < 0) {
u = -u;
sign = "-";
}
printf("%s%" PRIu32 ".%05" PRIu32, sign, u >> fixPrecision,
((uint32_t)(fix2double(u) * 100000 + 0.5)) % 100000);
} }
int32_t fix_Sin(int32_t i) int32_t fix_Cos(int32_t i, int32_t q)
{ {
return double2fix(sin(turn2rad(fix2double(i)))); return double2fix(cos(turn2rad(fix2double(i, q))), q);
} }
int32_t fix_Cos(int32_t i) int32_t fix_Tan(int32_t i, int32_t q)
{ {
return double2fix(cos(turn2rad(fix2double(i)))); return double2fix(tan(turn2rad(fix2double(i, q))), q);
} }
int32_t fix_Tan(int32_t i) int32_t fix_ASin(int32_t i, int32_t q)
{ {
return double2fix(tan(turn2rad(fix2double(i)))); return double2fix(rad2turn(asin(fix2double(i, q))), q);
} }
int32_t fix_ASin(int32_t i) int32_t fix_ACos(int32_t i, int32_t q)
{ {
return double2fix(rad2turn(asin(fix2double(i)))); return double2fix(rad2turn(acos(fix2double(i, q))), q);
} }
int32_t fix_ACos(int32_t i) int32_t fix_ATan(int32_t i, int32_t q)
{ {
return double2fix(rad2turn(acos(fix2double(i)))); return double2fix(rad2turn(atan(fix2double(i, q))), q);
} }
int32_t fix_ATan(int32_t i) int32_t fix_ATan2(int32_t i, int32_t j, int32_t q)
{ {
return double2fix(rad2turn(atan(fix2double(i)))); return double2fix(rad2turn(atan2(fix2double(i, q), fix2double(j, q))), q);
} }
int32_t fix_ATan2(int32_t i, int32_t j) int32_t fix_Mul(int32_t i, int32_t j, int32_t q)
{ {
return double2fix(rad2turn(atan2(fix2double(i), fix2double(j)))); return double2fix(fix2double(i, q) * fix2double(j, q), q);
} }
int32_t fix_Mul(int32_t i, int32_t j) int32_t fix_Div(int32_t i, int32_t j, int32_t q)
{ {
return double2fix(fix2double(i) * fix2double(j)); return double2fix(fix2double(i, q) / fix2double(j, q), q);
} }
int32_t fix_Div(int32_t i, int32_t j) int32_t fix_Mod(int32_t i, int32_t j, int32_t q)
{ {
return double2fix(fix2double(i) / fix2double(j)); return double2fix(fmod(fix2double(i, q), fix2double(j, q)), q);
} }
int32_t fix_Mod(int32_t i, int32_t j) int32_t fix_Pow(int32_t i, int32_t j, int32_t q)
{ {
return double2fix(fmod(fix2double(i), fix2double(j))); return double2fix(pow(fix2double(i, q), fix2double(j, q)), q);
} }
int32_t fix_Pow(int32_t i, int32_t j) int32_t fix_Log(int32_t i, int32_t j, int32_t q)
{ {
return double2fix(pow(fix2double(i), fix2double(j))); return double2fix(log(fix2double(i, q)) / log(fix2double(j, q)), q);
} }
int32_t fix_Log(int32_t i, int32_t j) int32_t fix_Round(int32_t i, int32_t q)
{ {
return double2fix(log(fix2double(i)) / log(fix2double(j))); return double2fix(round(fix2double(i, q)), q);
} }
int32_t fix_Round(int32_t i) int32_t fix_Ceil(int32_t i, int32_t q)
{ {
return double2fix(round(fix2double(i))); return double2fix(ceil(fix2double(i, q)), q);
} }
int32_t fix_Ceil(int32_t i) int32_t fix_Floor(int32_t i, int32_t q)
{ {
return double2fix(ceil(fix2double(i))); return double2fix(floor(fix2double(i, q)), q);
}
int32_t fix_Floor(int32_t i)
{
return double2fix(floor(fix2double(i)));
} }

View File

@@ -550,6 +550,7 @@ enum {
%token T_OP_BANK "BANK" %token T_OP_BANK "BANK"
%token T_OP_ALIGN "ALIGN" %token T_OP_ALIGN "ALIGN"
%token T_OP_SIZEOF "SIZEOF" T_OP_STARTOF "STARTOF" %token T_OP_SIZEOF "SIZEOF" T_OP_STARTOF "STARTOF"
%token T_OP_SIN "SIN" T_OP_COS "COS" T_OP_TAN "TAN" %token T_OP_SIN "SIN" T_OP_COS "COS" T_OP_TAN "TAN"
%token T_OP_ASIN "ASIN" T_OP_ACOS "ACOS" T_OP_ATAN "ATAN" T_OP_ATAN2 "ATAN2" %token T_OP_ASIN "ASIN" T_OP_ACOS "ACOS" T_OP_ATAN "ATAN" T_OP_ATAN2 "ATAN2"
%token T_OP_FDIV "FDIV" %token T_OP_FDIV "FDIV"
@@ -559,6 +560,7 @@ enum {
%token T_OP_LOG "LOG" %token T_OP_LOG "LOG"
%token T_OP_ROUND "ROUND" %token T_OP_ROUND "ROUND"
%token T_OP_CEIL "CEIL" T_OP_FLOOR "FLOOR" %token T_OP_CEIL "CEIL" T_OP_FLOOR "FLOOR"
%type <constValue> opt_q_arg
%token T_OP_HIGH "HIGH" T_OP_LOW "LOW" %token T_OP_HIGH "HIGH" T_OP_LOW "LOW"
%token T_OP_ISCONST "ISCONST" %token T_OP_ISCONST "ISCONST"
@@ -1466,50 +1468,50 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); }
lexer_ToggleStringExpansion(true); lexer_ToggleStringExpansion(true);
} }
| T_OP_ROUND T_LPAREN const T_RPAREN { | T_OP_ROUND T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Round($3)); rpn_Number(&$$, fix_Round($3, $4));
} }
| T_OP_CEIL T_LPAREN const T_RPAREN { | T_OP_CEIL T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Ceil($3)); rpn_Number(&$$, fix_Ceil($3, $4));
} }
| T_OP_FLOOR T_LPAREN const T_RPAREN { | T_OP_FLOOR T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Floor($3)); rpn_Number(&$$, fix_Floor($3, $4));
} }
| T_OP_FDIV T_LPAREN const T_COMMA const T_RPAREN { | T_OP_FDIV T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Div($3, $5)); rpn_Number(&$$, fix_Div($3, $5, $6));
} }
| T_OP_FMUL T_LPAREN const T_COMMA const T_RPAREN { | T_OP_FMUL T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Mul($3, $5)); rpn_Number(&$$, fix_Mul($3, $5, $6));
} }
| T_OP_FMOD T_LPAREN const T_COMMA const T_RPAREN { | T_OP_FMOD T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Mod($3, $5)); rpn_Number(&$$, fix_Mod($3, $5, $6));
} }
| T_OP_POW T_LPAREN const T_COMMA const T_RPAREN { | T_OP_POW T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Pow($3, $5)); rpn_Number(&$$, fix_Pow($3, $5, $6));
} }
| T_OP_LOG T_LPAREN const T_COMMA const T_RPAREN { | T_OP_LOG T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Log($3, $5)); rpn_Number(&$$, fix_Log($3, $5, $6));
} }
| T_OP_SIN T_LPAREN const T_RPAREN { | T_OP_SIN T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Sin($3)); rpn_Number(&$$, fix_Sin($3, $4));
} }
| T_OP_COS T_LPAREN const T_RPAREN { | T_OP_COS T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Cos($3)); rpn_Number(&$$, fix_Cos($3, $4));
} }
| T_OP_TAN T_LPAREN const T_RPAREN { | T_OP_TAN T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Tan($3)); rpn_Number(&$$, fix_Tan($3, $4));
} }
| T_OP_ASIN T_LPAREN const T_RPAREN { | T_OP_ASIN T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_ASin($3)); rpn_Number(&$$, fix_ASin($3, $4));
} }
| T_OP_ACOS T_LPAREN const T_RPAREN { | T_OP_ACOS T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_ACos($3)); rpn_Number(&$$, fix_ACos($3, $4));
} }
| T_OP_ATAN T_LPAREN const T_RPAREN { | T_OP_ATAN T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_ATan($3)); rpn_Number(&$$, fix_ATan($3, $4));
} }
| T_OP_ATAN2 T_LPAREN const T_COMMA const T_RPAREN { | T_OP_ATAN2 T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_ATan2($3, $5)); rpn_Number(&$$, fix_ATan2($3, $5, $6));
} }
| T_OP_STRCMP T_LPAREN string T_COMMA string T_RPAREN { | T_OP_STRCMP T_LPAREN string T_COMMA string T_RPAREN {
rpn_Number(&$$, strcmp($3, $5)); rpn_Number(&$$, strcmp($3, $5));
@@ -1536,7 +1538,7 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); }
uconst : const { uconst : const {
$$ = $1; $$ = $1;
if ($$ < 0) if ($$ < 0)
fatalerror("Constant mustn't be negative: %d\n", $1); fatalerror("Constant must not be negative: %d\n", $1);
} }
; ;
@@ -1549,6 +1551,17 @@ const_no_str : relocexpr_no_str { $$ = rpn_GetConstVal(&$1); }
const_8bit : reloc_8bit { $$ = rpn_GetConstVal(&$1); } const_8bit : reloc_8bit { $$ = rpn_GetConstVal(&$1); }
; ;
opt_q_arg : %empty { $$ = fix_Precision(); }
| T_COMMA const {
if ($2 >= 1 && $2 <= 31) {
$$ = $2;
} else {
error("Fixed-point precision must be between 1 and 31\n");
$$ = fix_Precision();
}
}
;
string : T_STRING string : T_STRING
| T_OP_STRSUB T_LPAREN string T_COMMA const T_COMMA uconst T_RPAREN { | T_OP_STRSUB T_LPAREN string T_COMMA const T_COMMA uconst T_RPAREN {
size_t len = strlenUTF8($3); size_t len = strlenUTF8($3);

View File

@@ -0,0 +1,27 @@
MACRO compare
print "\3: "
if _NARG == 4
def v1 = \3(\4q\1, \1)
def v2 = \3(\4q\2, \2)
elif _NARG == 5
def v1 = \3(\4q\1, \5q\1, \1)
def v2 = \3(\4q\2, \5q\2, \2)
endc
opt Q\1
print "{.4f:v1} == "
opt Q\2
println "{.4f:v2}"
ENDM
compare 8, 16, mul, 6.0, 7.0
compare 12, 24, div, 115.625, 9.25
compare 7, 14, pow, 3.5, 5.5
compare 4, 8, sin, 0.25
compare 5, 9, cos, 0.75
compare 6, 10, asin, 1.0
compare 7, 11, acos, 0.0
compare 3, 6, round, 1.75
compare 10, 20, ceil, 123.4
compare 13, 17, floor, 567.8

View File

View File

@@ -0,0 +1,10 @@
mul: 42.0000 == 42.0000
div: 12.5000 == 12.5000
pow: 982.5938 == 982.5943
sin: 1.0000 == 1.0000
cos: 0.0000 == 0.0000
asin: 0.2500 == 0.2500
acos: 0.2500 == 0.2500
round: 2.0000 == 2.0000
ceil: 124.0000 == 124.0000
floor: 567.0000 == 567.0000