added arcsin and arccos
added a stack for temporaries, otherwise it became to tedious to keep track which function uses which temporary for later
This commit is contained in:
parent
b56523fadf
commit
2903579efd
@ -180,7 +180,7 @@ ApplicationWindow
|
|||||||
["", "", "", ""],
|
["", "", "", ""],
|
||||||
["", "", "", ""],
|
["", "", "", ""],
|
||||||
["R▲", "", "", ""],
|
["R▲", "", "", ""],
|
||||||
["", "", "tan<sup>−1</sup>", "►deg"],
|
["sin<sup>−1</sup>", "cos<sup>−1</sup>", "tan<sup>−1</sup>", "►deg"],
|
||||||
["", "", "", ""]
|
["", "", "", ""]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
27
src/calc.c
27
src/calc.c
@ -23,6 +23,10 @@
|
|||||||
#include "calc.h"
|
#include "calc.h"
|
||||||
#include "stack_debug.h"
|
#include "stack_debug.h"
|
||||||
|
|
||||||
|
#ifdef DESKTOP
|
||||||
|
#include <assert.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
__xdata dec80 StoredDecn;
|
__xdata dec80 StoredDecn;
|
||||||
__xdata dec80 LastX;
|
__xdata dec80 LastX;
|
||||||
|
|
||||||
@ -163,21 +167,7 @@ void process_cmd(char cmd){
|
|||||||
case '<':{ //use as +/- and sqrt
|
case '<':{ //use as +/- and sqrt
|
||||||
if (IsShiftedUp){ //take sqrt
|
if (IsShiftedUp){ //take sqrt
|
||||||
IsShiftedUp = 0;
|
IsShiftedUp = 0;
|
||||||
if (decn_is_zero(&stack(STACK_X))){
|
do_unary_op(sqrt_decn);
|
||||||
//sqrt(0) = 0
|
|
||||||
} else if (!decn_is_nan(&stack(STACK_X))){
|
|
||||||
copy_decn(&LastX, &stack(STACK_X)); //save LastX
|
|
||||||
copy_decn(&AccDecn, &stack(STACK_X));
|
|
||||||
if (AccDecn.exponent < 0){ //negative
|
|
||||||
set_dec80_NaN(&stack(STACK_X));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//b = 0.5
|
|
||||||
set_dec80_zero(&BDecn);
|
|
||||||
BDecn.lsu[0] = 5;
|
|
||||||
pow_decn();
|
|
||||||
copy_decn(&stack(STACK_X), &AccDecn);
|
|
||||||
}
|
|
||||||
} else { // +/-
|
} else { // +/-
|
||||||
if (!decn_is_nan(&stack(STACK_X))){
|
if (!decn_is_nan(&stack(STACK_X))){
|
||||||
negate_decn(&stack(STACK_X));
|
negate_decn(&stack(STACK_X));
|
||||||
@ -215,7 +205,7 @@ void process_cmd(char cmd){
|
|||||||
if (IsShiftedUp){
|
if (IsShiftedUp){
|
||||||
do_unary_op(sin_decn);
|
do_unary_op(sin_decn);
|
||||||
} else if (IsShiftedDown){
|
} else if (IsShiftedDown){
|
||||||
// do_unary_op(arcsin_decn);
|
do_unary_op(arcsin_decn);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
//////////
|
//////////
|
||||||
@ -223,7 +213,7 @@ void process_cmd(char cmd){
|
|||||||
if (IsShiftedUp){
|
if (IsShiftedUp){
|
||||||
do_unary_op(cos_decn);
|
do_unary_op(cos_decn);
|
||||||
} else if (IsShiftedDown){
|
} else if (IsShiftedDown){
|
||||||
// do_unary_op(arccos_decn);
|
do_unary_op(arccos_decn);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
//////////
|
//////////
|
||||||
@ -283,6 +273,9 @@ void process_cmd(char cmd){
|
|||||||
} //switch(cmd)
|
} //switch(cmd)
|
||||||
IsShiftedUp = 0;
|
IsShiftedUp = 0;
|
||||||
IsShiftedDown = 0;
|
IsShiftedDown = 0;
|
||||||
|
#ifdef DESKTOP
|
||||||
|
assert(TmpStackPtr == 0); // there should be no items on the temporaries stack after one global operation
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,10 +67,14 @@ static const uint8_t num_digits_display = 16;
|
|||||||
|
|
||||||
dec80 AccDecn;
|
dec80 AccDecn;
|
||||||
__idata dec80 BDecn;
|
__idata dec80 BDecn;
|
||||||
__idata dec80 TmpDecn; //used by add_decn() and mult_decn()
|
__idata dec80 TmpDecn; //used by add_decn() and mult_decn() and sqrt_decn()
|
||||||
__idata dec80 Tmp2Decn; //used by recip_decn() and ln_decn() and sincos_decn()
|
__idata dec80 Tmp2Decn; //used by recip_decn() and ln_decn() and sincos_decn() and sqrt_decn()
|
||||||
__xdata dec80 Tmp3Decn; //used by recip_decn() and ln_decn() and sincos_decn()
|
__xdata dec80 Tmp3Decn; //used by recip_decn() and ln_decn() and sincos_decn() and sqrt_decn()
|
||||||
__xdata dec80 Tmp4Decn; //used by div_decn() and pow_decn() and sincos_decn()
|
__xdata dec80 Tmp4Decn; //used by sincos_decn()
|
||||||
|
|
||||||
|
__xdata dec80 TmpStackDecn[4];
|
||||||
|
#define TMP_STACK_SIZE (sizeof TmpStackDecn / sizeof TmpStackDecn[0])
|
||||||
|
__idata uint8_t TmpStackPtr;
|
||||||
|
|
||||||
__xdata char Buf[DECN_BUF_SIZE];
|
__xdata char Buf[DECN_BUF_SIZE];
|
||||||
|
|
||||||
@ -89,11 +93,36 @@ const dec80 DECN_2PI = {
|
|||||||
0, {62, 83, 18, 53, 7, 17, 95, 86, 48}
|
0, {62, 83, 18, 53, 7, 17, 95, 86, 48}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// pi/2
|
||||||
|
const dec80 DECN_PI2 = {
|
||||||
|
0, {15, 70, 79, 63, 26, 79, 48, 96, 62}
|
||||||
|
};
|
||||||
|
|
||||||
// 180/pi = 1rad in degree
|
// 180/pi = 1rad in degree
|
||||||
const dec80 DECN_1RAD = {
|
const dec80 DECN_1RAD = {
|
||||||
1, {57, 29, 57, 79, 51, 30, 82, 32, 9}
|
1, {57, 29, 57, 79, 51, 30, 82, 32, 9}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void st_push_decn(const dec80 * const src)
|
||||||
|
{
|
||||||
|
copy_decn(&TmpStackDecn[TmpStackPtr], src);
|
||||||
|
TmpStackPtr++;
|
||||||
|
assert(TmpStackPtr < TMP_STACK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void st_pop_decn(dec80 * const dst)
|
||||||
|
{
|
||||||
|
assert(TmpStackPtr >= 1);
|
||||||
|
TmpStackPtr--;
|
||||||
|
if (dst) copy_decn(dst, &TmpStackDecn[TmpStackPtr]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void st_load_decn(dec80 * const dst)
|
||||||
|
{
|
||||||
|
assert(TmpStackPtr >= 1);
|
||||||
|
copy_decn(dst, &TmpStackDecn[TmpStackPtr - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
void copy_decn(dec80* const dest, const dec80* const src){
|
void copy_decn(dec80* const dest, const dec80* const src){
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
|
|
||||||
@ -599,6 +628,7 @@ void add_decn(void){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//save b for restoring later
|
//save b for restoring later
|
||||||
|
//n.b. don't use TmpStackDecn here, it is called quite often. So you'd need to increase TMP_STACK_SIZE
|
||||||
copy_decn(&TmpDecn, &BDecn);
|
copy_decn(&TmpDecn, &BDecn);
|
||||||
//handle cases where signs differ
|
//handle cases where signs differ
|
||||||
if (AccDecn.exponent < 0 && BDecn.exponent >= 0){
|
if (AccDecn.exponent < 0 && BDecn.exponent >= 0){
|
||||||
@ -805,7 +835,6 @@ void mult_decn(void){
|
|||||||
|
|
||||||
void recip_decn(void){
|
void recip_decn(void){
|
||||||
#define CURR_RECIP Tmp2Decn //copy of x, holds current 1/x estimate
|
#define CURR_RECIP Tmp2Decn //copy of x, holds current 1/x estimate
|
||||||
#define X_COPY Tmp3Decn //holds copy of original x
|
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
exp_t initial_exp;
|
exp_t initial_exp;
|
||||||
//check divide by zero
|
//check divide by zero
|
||||||
@ -821,7 +850,7 @@ void recip_decn(void){
|
|||||||
//normalize
|
//normalize
|
||||||
remove_leading_zeros(&AccDecn);
|
remove_leading_zeros(&AccDecn);
|
||||||
//store copy of x
|
//store copy of x
|
||||||
copy_decn(&X_COPY, &AccDecn);
|
st_push_decn(&AccDecn);
|
||||||
//get initial exponent of estimate for 1/x
|
//get initial exponent of estimate for 1/x
|
||||||
initial_exp = get_exponent(&AccDecn);
|
initial_exp = get_exponent(&AccDecn);
|
||||||
#ifdef DEBUG_DIV
|
#ifdef DEBUG_DIV
|
||||||
@ -854,7 +883,7 @@ void recip_decn(void){
|
|||||||
printf("%2d: %s\n", i, Buf);
|
printf("%2d: %s\n", i, Buf);
|
||||||
#endif
|
#endif
|
||||||
//Accum *= x_copy
|
//Accum *= x_copy
|
||||||
copy_decn(&BDecn, &X_COPY);
|
st_load_decn(&BDecn);
|
||||||
mult_decn();
|
mult_decn();
|
||||||
#ifdef DEBUG_DIV
|
#ifdef DEBUG_DIV
|
||||||
decn_to_str_complete(&AccDecn);
|
decn_to_str_complete(&AccDecn);
|
||||||
@ -881,24 +910,20 @@ void recip_decn(void){
|
|||||||
//new_est(Accum) = recip + (1 - recip*x)*recip, where recip is current recip estimate
|
//new_est(Accum) = recip + (1 - recip*x)*recip, where recip is current recip estimate
|
||||||
copy_decn(&CURR_RECIP, &AccDecn);
|
copy_decn(&CURR_RECIP, &AccDecn);
|
||||||
}
|
}
|
||||||
|
st_pop_decn(0);
|
||||||
|
|
||||||
//try not to pollute namespace
|
//try not to pollute namespace
|
||||||
#undef CURR_RECIP
|
#undef CURR_RECIP
|
||||||
#undef X_COPY
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void div_decn(void){
|
void div_decn(void){
|
||||||
#define ACC_COPY Tmp4Decn //holds copy of original acc
|
|
||||||
//store copy of acc for final multiply by 1/x
|
//store copy of acc for final multiply by 1/x
|
||||||
copy_decn(&ACC_COPY, &AccDecn);
|
st_push_decn(&AccDecn);
|
||||||
copy_decn(&AccDecn, &BDecn);
|
copy_decn(&AccDecn, &BDecn);
|
||||||
recip_decn();
|
recip_decn();
|
||||||
//Accum now holds 1/x, multiply by original acc to complete division
|
//Accum now holds 1/x, multiply by original acc to complete division
|
||||||
copy_decn(&BDecn, &ACC_COPY);
|
st_pop_decn(&BDecn);
|
||||||
mult_decn();
|
mult_decn();
|
||||||
|
|
||||||
//try not to pollute namespace
|
|
||||||
#undef ACC_COPY
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1256,9 +1281,9 @@ void pow_decn(void) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//calculate AccDecn = AccDecn ^ BDecn
|
//calculate AccDecn = AccDecn ^ BDecn
|
||||||
copy_decn(&Tmp4Decn, &BDecn); //save b
|
st_push_decn(&BDecn);
|
||||||
ln_decn();
|
ln_decn();
|
||||||
copy_decn(&BDecn, &Tmp4Decn); //restore b
|
st_pop_decn(&BDecn);
|
||||||
mult_decn(); //accum = b*ln(accum)
|
mult_decn(); //accum = b*ln(accum)
|
||||||
exp_decn();
|
exp_decn();
|
||||||
}
|
}
|
||||||
@ -1379,6 +1404,29 @@ void tan_decn(void) {
|
|||||||
void arctan_decn(void) {
|
void arctan_decn(void) {
|
||||||
sincos_decn(1);
|
sincos_decn(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// see W.E. Egbert, "Personal Calculator Algorithms III: Inverse Trigonometric Functions"
|
||||||
|
void arcsin_decn(void) {
|
||||||
|
st_push_decn(&AccDecn);
|
||||||
|
copy_decn(&BDecn, &AccDecn);
|
||||||
|
mult_decn();
|
||||||
|
negate_decn(&AccDecn);
|
||||||
|
copy_decn(&BDecn, &DECN_1);
|
||||||
|
add_decn();
|
||||||
|
sqrt_decn();
|
||||||
|
recip_decn();
|
||||||
|
st_pop_decn(&BDecn);
|
||||||
|
mult_decn();
|
||||||
|
|
||||||
|
sincos_decn(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void arccos_decn(void) {
|
||||||
|
arcsin_decn();
|
||||||
|
negate_decn(&AccDecn);
|
||||||
|
copy_decn(&BDecn, &DECN_PI2);
|
||||||
|
add_decn();
|
||||||
|
}
|
||||||
#undef SIN
|
#undef SIN
|
||||||
#undef COS
|
#undef COS
|
||||||
#undef THETA
|
#undef THETA
|
||||||
@ -1400,6 +1448,24 @@ void pi_decn(void) {
|
|||||||
mult_decn();
|
mult_decn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sqrt_decn(void) {
|
||||||
|
if (decn_is_zero(&AccDecn)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (decn_is_nan(&AccDecn)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (AccDecn.exponent < 0){ //negative
|
||||||
|
set_dec80_NaN(&AccDecn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
st_push_decn(&BDecn); // sqrt should behave like an unary operation
|
||||||
|
//b = 0.5
|
||||||
|
set_dec80_zero(&BDecn);
|
||||||
|
BDecn.lsu[0] = 5;
|
||||||
|
pow_decn();
|
||||||
|
st_pop_decn(&BDecn);
|
||||||
|
}
|
||||||
|
|
||||||
static void set_str_error(void){
|
static void set_str_error(void){
|
||||||
Buf[0] = 'E';
|
Buf[0] = 'E';
|
||||||
|
@ -64,7 +64,7 @@ void copy_decn(dec80* const dest, const dec80* const src);
|
|||||||
|
|
||||||
extern dec80 AccDecn;
|
extern dec80 AccDecn;
|
||||||
extern __idata dec80 BDecn;
|
extern __idata dec80 BDecn;
|
||||||
extern __xdata dec80 Tmp4Decn;
|
extern __idata uint8_t TmpStackPtr;
|
||||||
|
|
||||||
void build_dec80(__xdata const char* signif_str, __xdata exp_t exponent);
|
void build_dec80(__xdata const char* signif_str, __xdata exp_t exponent);
|
||||||
|
|
||||||
@ -85,11 +85,14 @@ void log10_decn(void);
|
|||||||
void exp_decn(void);
|
void exp_decn(void);
|
||||||
void exp10_decn(void);
|
void exp10_decn(void);
|
||||||
void pow_decn(void);
|
void pow_decn(void);
|
||||||
|
void sqrt_decn(void);
|
||||||
|
|
||||||
void sin_decn(void);
|
void sin_decn(void);
|
||||||
void cos_decn(void);
|
void cos_decn(void);
|
||||||
void tan_decn(void);
|
void tan_decn(void);
|
||||||
void arctan_decn(void);
|
void arctan_decn(void);
|
||||||
|
void arcsin_decn(void);
|
||||||
|
void arccos_decn(void);
|
||||||
void to_degree_decn(void);
|
void to_degree_decn(void);
|
||||||
void to_radian_decn(void);
|
void to_radian_decn(void);
|
||||||
void pi_decn(void);
|
void pi_decn(void);
|
||||||
@ -111,12 +114,16 @@ void decn_to_str_complete(const dec80* x);
|
|||||||
void build_decn_at(dec80* dest, const char* signif_str, exp_t exponent);
|
void build_decn_at(dec80* dest, const char* signif_str, exp_t exponent);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef DESKTOP
|
||||||
#define PRINT_DEC80(n, v) \
|
#define PRINT_DEC80(n, v) \
|
||||||
printf(n " %d %5d: ", v.exponent < 0, get_exponent(&v)); \
|
printf(n " %d %5d: ", v.exponent < 0, get_exponent(&v)); \
|
||||||
for (int i = 0; i < DEC80_NUM_LSU; i++) { \
|
for (int i = 0; i < DEC80_NUM_LSU; i++) { \
|
||||||
printf("%02d ", v.lsu[i]); \
|
printf("%02d ", v.lsu[i]); \
|
||||||
} \
|
} \
|
||||||
fputc('\n', stdout);
|
fputc('\n', stdout);
|
||||||
|
#else
|
||||||
|
#define PRINT_DEC80(n, v)
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,18 @@ static void atan_test(
|
|||||||
trig_test(arctan_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return atan(x);}, a_str, a_exp, rtol, atol);
|
trig_test(arctan_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return atan(x);}, a_str, a_exp, rtol, atol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void asin_test(
|
||||||
|
const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3)
|
||||||
|
{
|
||||||
|
trig_test(arcsin_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return asin(x);}, a_str, a_exp, rtol, atol);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void acos_test(
|
||||||
|
const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3)
|
||||||
|
{
|
||||||
|
trig_test(arccos_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return acos(x);}, a_str, a_exp, rtol, atol);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const char * const pi = "3.141592653589793239";
|
const char * const pi = "3.141592653589793239";
|
||||||
const char * const pi_threequarters = "2.356194490192344929";
|
const char * const pi_threequarters = "2.356194490192344929";
|
||||||
@ -167,3 +179,25 @@ TEST_CASE("arctan") {
|
|||||||
atan_test("-3.0", 0);
|
atan_test("-3.0", 0);
|
||||||
atan_test("0", 0, -1);
|
atan_test("0", 0, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("arcsin") {
|
||||||
|
asin_test("0.001", 0, -1);
|
||||||
|
asin_test("-0.001", 0, -1);
|
||||||
|
asin_test("0.7", 0);
|
||||||
|
asin_test("-0.7", 0);
|
||||||
|
asin_test("0.1", 0);
|
||||||
|
asin_test("-0.1", 0);
|
||||||
|
asin_test("0.9", 0);
|
||||||
|
asin_test("-0.9", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("arccos") {
|
||||||
|
acos_test("0.001", 0);
|
||||||
|
acos_test("-0.001", 0);
|
||||||
|
acos_test("0.7", 0);
|
||||||
|
acos_test("-0.7", 0);
|
||||||
|
acos_test("0.1", 0);
|
||||||
|
acos_test("-0.1", 0);
|
||||||
|
acos_test("0.9", 0);
|
||||||
|
acos_test("-0.9", 0);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user