// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
/*
* decn.c
*
* Created on: Mar 21, 2019
* Author: jeffrey
*/
#include "../utils.h"
#include "../stack_debug.h"
#include "decn.h"
#define EXTRA_CHECKS
// #define DEBUG
// #define DEBUG_COMPARE_MAGN
// #define DEBUG_ADD
// #define DEBUG_MULT
// #define DEBUG_MULT_ALL //even more verbose
// #define DEBUG_DIV
// #define DEBUG_LOG
// #define DEBUG_LOG_ALL //even more verbose
// #define DEBUG_EXP
// #define DEBUG_EXP_ALL //even more verbose
// #define DEBUG_SQRT
#ifndef DESKTOP
//#undef EXTRA_CHECKS
#undef DEBUG
#undef DEBUG_COMPARE_MAGN
#undef DEBUG_ADD
#undef DEBUG_MULT
#undef DEBUG_DIV
#undef DEBUG_LOG
#undef DEBUG_LOG_ALL
#undef DEBUG_EXP
#undef DEBUG_EXP_ALL
#undef DEBUG_SQRT
#endif
#ifdef DESKTOP
#include
#endif
#ifdef DESKTOP
#include
#else
#define assert(x)
#endif
#ifdef DESKTOP
static const uint8_t num_digits_display = DEC80_NUM_LSU*2;
#else
static const uint8_t num_digits_display = 16;
#endif
dec80 AccDecn;
__idata dec80 BDecn;
__idata dec80 TmpDecn; //used by add_decn() and mult_decn() and sqrt_decn()
__idata dec80 Tmp2Decn; //used by recip_decn(), ln_decn(), exp_decn(), sqrt_decn(), and sincos_decn()
__idata dec80 Tmp3Decn; //used by ln_decn(), exp_decn(), sqrt_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];
//1 constant
const dec80 DECN_1 = {
0, {10, 0}
};
//ln(10) constant
const dec80 DECN_LN_10 = {
0, {23, 2, 58, 50, 92, 99, 40, 45, 68}
};
// 2 pi
const dec80 DECN_2PI = {
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
const dec80 DECN_1RAD = {
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){
uint8_t i;
stack_debug(0x01);
dest->exponent = src->exponent;
//copy nibbles
for (i = 0; i < DEC80_NUM_LSU; i++){
dest->lsu[i] = src->lsu[i];
}
}
exp_t get_exponent(const dec80* const x){
exp_t exponent = x->exponent;
#ifdef EXP16
if (exponent & 0x4000){ //negative
return exponent | 0x8000;
} else { //positive
return exponent & 0x7fff;
}
#else
if (exponent & 0x40){ //negative
return exponent | 0x80;
} else { //positive
return exponent & 0x7f;
}
#endif
}
static void set_exponent(dec80* acc, exp_t exponent, uint8_t num_is_neg){
#ifdef EXP16
if (num_is_neg){
exponent |= 0x8000;
} else {
exponent &= 0x7fff;
}
#else
if (num_is_neg){
exponent |= 0x80;
} else {
exponent &= 0x7f;
}
#endif
acc->exponent = exponent;
}
static void zero_remaining_dec80(dec80* dest, uint8_t digit100){
for ( ; digit100 < DEC80_NUM_LSU; digit100++){
dest->lsu[digit100] = 0;
}
}
static uint8_t shift_high = 0, shift_low = 0, shift_old = 0;
static uint8_t shift_i;
static void shift_right(dec80* x){
shift_high = shift_low = shift_old = 0;
for (shift_i = 0; shift_i < DEC80_NUM_LSU; shift_i++){
shift_high = x->lsu[shift_i] / 10;
shift_low = x->lsu[shift_i] % 10;
x->lsu[shift_i] = shift_high + ( shift_old*10);
shift_old = shift_low;
}
}
static void shift_left(dec80* x){
shift_high = shift_low = shift_old = 0;
for (shift_i = DEC80_NUM_LSU - 1; shift_i < 255; shift_i--){
shift_high = x->lsu[shift_i] / 10;
shift_low = x->lsu[shift_i] % 10;
x->lsu[shift_i] = shift_old + ( shift_low*10);
shift_old = shift_high;
}
}
static void remove_leading_zeros(dec80* x){
uint8_t digit100;
uint8_t is_negative = (x->exponent < 0);
exp_t exponent = get_exponent(x);
stack_debug(0x02);
//find first non-zero digit100
for (digit100 = 0; digit100 < DEC80_NUM_LSU; digit100++){
if (x->lsu[digit100] != 0){
break;
}
}
exponent -= digit100 * 2; //base 100
//copy digit100s left if needed
if (digit100 != 0 && digit100 < DEC80_NUM_LSU){ //found non-zero digit100
uint8_t i;
//copy digit100s
for (i = 0; digit100 < DEC80_NUM_LSU; i++, digit100++){
x->lsu[i] = x->lsu[digit100];
}
//zero out remaining digit100s, now that number left-aligned
zero_remaining_dec80(x, i);
}
//ensure MSdigit in MSdigit100 is > 0
if (x->lsu[0] < 10) {
shift_left(x);
exponent--;
}
//write back exponent
set_exponent(x, exponent, is_negative);
}
void build_dec80(__xdata const char* signif_str, __xdata exp_t exponent){
enum {
SIGN_ZERO,
SIGN_ZERO_SEEN_POINT,
SIGN_NEG_ZERO,
SIGN_NEG_ZERO_SEEN_POINT,
SIGN_POS,
SIGN_POS_SEEN_POINT,
SIGN_NEG,
SIGN_NEG_SEEN_POINT
};
#ifdef DESKTOP
static_assert(SIGN_ZERO < SIGN_NEG_ZERO, "do not change enum values");
#endif
#define SEEN_POINT(curr_sign) ((curr_sign) & 1)
#define IS_ZERO(curr_sign) ((curr_sign) <= SIGN_NEG_ZERO_SEEN_POINT)
#define IS_NEG(curr_sign) ((curr_sign) >= SIGN_NEG)
uint8_t i = 0;
uint8_t nibble_i = 0;
uint8_t save_nibble = 0;
int8_t num_lr_points = 0; //number of digits to the right (-) or left of decimal point
int8_t curr_sign = SIGN_ZERO;
//check first digit
if (signif_str[0] == '\0'){
set_dec80_zero(&AccDecn);
return;
} else if (signif_str[0] == '-'){
curr_sign = SIGN_NEG_ZERO;
i++;
}
//go through digits
while (1){
if (signif_str[i] == '.'){
if (!SEEN_POINT(curr_sign)){
//begin tracking number of digits to right of decimal point
curr_sign |= 1; //seen point
}
#ifdef EXTRA_CHECKS
else {
//multiple '.'s in string
#ifdef DEBUG
printf(" ERROR: multiple '.'s in string\n");
#endif
set_dec80_NaN(&AccDecn);
return;
}
#endif
} else if (signif_str[i] >= '1' && signif_str[i] <= '9'){
if (nibble_i < DEC80_NUM_LSU*2){
if (nibble_i & 1) { //odd
AccDecn.lsu[nibble_i/2] = save_nibble * 10 + (signif_str[i] - '0');
} else {
save_nibble = signif_str[i] - '0';
}
}
nibble_i++;
//track number of digits R of decimal point
//must do this before changing is_zero
if (num_lr_points == 0 &&
(curr_sign == SIGN_ZERO_SEEN_POINT || curr_sign == SIGN_NEG_ZERO_SEEN_POINT))
{
num_lr_points = -1;
}
//track sign
if (curr_sign == SIGN_ZERO){
curr_sign = SIGN_POS;
} else if (curr_sign == SIGN_ZERO_SEEN_POINT){
curr_sign = SIGN_POS_SEEN_POINT;
} else if (curr_sign == SIGN_NEG_ZERO){
curr_sign = SIGN_NEG;
} else if (curr_sign == SIGN_NEG_ZERO_SEEN_POINT){
curr_sign = SIGN_NEG_SEEN_POINT;
}
//track number digits L of decimal point
if (!SEEN_POINT(curr_sign)){ //haven't seen decimal point yet
num_lr_points++; //increase left count
}
} else if (signif_str[i] == '0'){
//make sure not a leading zero
if (!IS_ZERO(curr_sign)){ //non-zero value
if (nibble_i < DEC80_NUM_LSU*2){
if (nibble_i & 1) { //odd
AccDecn.lsu[nibble_i/2] = save_nibble * 10 + 0;
} else {
save_nibble = 0;
}
}
nibble_i++;
}
//track number digits L/R of decimal point
if (SEEN_POINT(curr_sign)){
if (IS_ZERO(curr_sign)){ //tracking 0s to right of point
if (num_lr_points == 0){ //no left count exists
num_lr_points = -2;
} else if (num_lr_points < 0){ //continue tracking count
num_lr_points--; //increase right count
}
}
} else { //haven't seen decimal point yet
if (!IS_ZERO(curr_sign)){ //not a leading zero
num_lr_points++; //increase left count
}
}
} else if (signif_str[i] == '\0'){ //done
if (IS_ZERO(curr_sign)){
//zero
set_dec80_zero(&AccDecn);
return;
} else {
//not zero
exp_t new_exponent = exponent;
//write out saved nibble, if it exists
// (saved while nibble_i even, nibble_i then incremented to odd)
if (nibble_i & 1){ //odd
AccDecn.lsu[nibble_i/2] = save_nibble * 10;
nibble_i++; //increment for zeroing out
}
//zero out any old data
zero_remaining_dec80(&AccDecn, nibble_i/2);
// adjust exponent for left-aligned significand input
// or for number of digits past decimal point
if (num_lr_points > 0){ //left count exists
new_exponent = exponent + (num_lr_points - 1); //1 digit left of implicit point
//overflow is checked later, should be impossible to overflow int16_t:
assert(new_exponent >= exponent);
} else if (num_lr_points < 0) { //right count exists
// (num_lr_points represents exponent shift)
// (this ends up being a subtraction)
new_exponent = exponent + num_lr_points;
//underflow is checked later, should be impossible to overflow int16_t:
assert(new_exponent <= exponent);
}
//check for over/underflow of exponent
exponent = new_exponent;
#ifdef EXTRA_CHECKS
if (exponent > DEC80_MAX_EXP || exponent < DEC80_MIN_EXP){
#ifdef DEBUG
printf(" over/underflow (new_exp, exp)=(%d,%d)\n",
new_exponent, exponent);
#endif
set_dec80_NaN(&AccDecn);
return;
}
#endif
//set negative bit
set_exponent(&AccDecn, exponent, IS_NEG(curr_sign));
//normalize
remove_leading_zeros(&AccDecn);
#ifdef DEBUG
printf(" num_lr_points (%d), new_exp (%d), sign (%d), exp (%d)\n",
num_lr_points, new_exponent, curr_sign, exponent);
printf(" ");
for (i = 0; i < DEC80_NUM_LSU; i++){
printf("%02d,", AccDecn.lsu[i]);
}
printf("\n");
#endif
return;
}
} else { //invalid character
#ifdef DEBUG
printf(" invalid character %c at i=%d\n", signif_str[i], i);
#endif
set_dec80_NaN(&AccDecn);
return;
}
i++;
assert(i < DECN_BUF_SIZE);
} // while 1
#undef SEEN_POINT
#undef IS_ZERO
#undef IS_NEG
}
void set_dec80_zero(dec80* dest){
uint8_t i;
//clear exponent
dest->exponent = 0;
//clear nibbles
for (i = 0; i < DEC80_NUM_LSU; i++){
dest->lsu[i] = 0;
}
}
uint8_t decn_is_zero(const dec80* x){
uint8_t i;
for (i = 0; i < DEC80_NUM_LSU; i++){
if (x->lsu[i] != 0){
return 0;
}
}
return 1;
}
uint8_t decn_is_nan(const dec80* x){
uint8_t i;
if (x->exponent != DEC80_NAN_EXP){
return 0;
}
for (i = 0; i < DEC80_NUM_LSU; i++){
if (x->lsu[i] != 0xff){
return 0;
}
}
return 1;
}
#ifdef EXTRA_CHECKS
void set_dec80_NaN(dec80* dest){
uint8_t i;
//set exponent to special val
dest->exponent = DEC80_NAN_EXP;
//set all nibbles
for (i = 0; i < DEC80_NUM_LSU; i++){
dest->lsu[i] = 0xff;
}
}
#endif
void negate_decn(dec80* x){
#ifdef EXP16
static const exp_t xor_val = -(0x7fff) - 1;
#else
static const exp_t xor_val = -(0x7f) - 1;
#endif
if (decn_is_nan(x)){
return;
}
(x->exponent) ^= xor_val;
}
static int8_t compare_magn(void){ //returns ab: 1
uint8_t a_i, b_i;
exp_t a_exp=0, b_exp=0;
int8_t a_signif_b = 0; //ab: 1
static __xdata dec80 a_tmp, b_tmp;
//copy
copy_decn(&a_tmp, &AccDecn);
copy_decn(&b_tmp, &BDecn);
//normalize
remove_leading_zeros(&a_tmp);
remove_leading_zeros(&b_tmp);
//compare signifcands while tracking magnitude
for (
a_i = 0, b_i = 0;
a_i < DEC80_NUM_LSU;
a_i++, b_i++, a_exp+=2, b_exp+=2
)
{
//set signif. inequality if this is first digit that is different
if (a_signif_b == 0 && (a_tmp.lsu[a_i] < b_tmp.lsu[b_i])){
#ifdef DEBUG_COMPARE_MAGN
printf("a_signif_b -1: a.lsu[%d]=%d, b.lsu[%d]=%d\n",
a_i, a_tmp.lsu[a_i], b_i, b_tmp.lsu[b_i]);
#endif
a_signif_b = -1;
} else if (a_signif_b == 0 && (a_tmp.lsu[a_i] > b_tmp.lsu[b_i])){
#ifdef DEBUG_COMPARE_MAGN
printf("a_signif_b 1: a.lsu[%d]=%d, b.lsu[%d]=%d\n",
a_i, a_tmp.lsu[a_i], b_i, b_tmp.lsu[b_i]);
#endif
a_signif_b = 1;
}
}
//calculate exponents
a_exp += get_exponent(&a_tmp);
b_exp += get_exponent(&b_tmp);
//compare exponents
if (a_exp > b_exp){
#ifdef DEBUG
printf("a_exp > b_exp\n");
#endif
return 1;
} else if (a_exp < b_exp){
#ifdef DEBUG
printf("a_exp < b_exp\n");
#endif
return -1;
}
//exponents equal, compare by significand
#ifdef DEBUG
printf("a_signif_b (%d)\n", a_signif_b);
#endif
return a_signif_b;
}
#ifdef USE_COMPARE_DECN
static int8_t compare_decn(void){ //returns ab: 1
int8_t is_neg;
//handle zero special cases
if (decn_is_zero(&AccDecn) && decn_is_zero(&BDecn)){
return 0;
} else if (decn_is_zero(&AccDecn)){
if (BDecn.exponent < 0){
return 1;
} else {
return -1;
}
} else if (decn_is_zero(&BDecn)){
if (AccDecn.exponent < 0){
return -1;
} else {
return 1;
}
}
//handle cases where signs differ
if (AccDecn.exponent < 0 && BDecn.exponent > 0){
return -1;
} else if (AccDecn.exponent > 0 && BDecn.exponent < 0){
return 1;
}
//signs must now be the same, either both pos, or both neg
is_neg = (AccDecn.exponent < 0 ? -1 : 1);
return is_neg * compare_magn();
}
#endif //USE_COMPARE_DECN
//WARNING: for add_decn() and sub_mag() functions only
//acc and the number from which exponent was taken MUST be stripped of leading 0s first
//rescales acc up to exponent (increase exponent of acc, while shifting right)
//the actual value of acc->exponent remains unchanged
static void _incr_exp(dec80* acc, exp_t exponent){
exp_t curr_exp = get_exponent(acc);
#ifdef DEBUG_ADD
uint8_t is_neg = (acc->exponent < 0);
printf(" (is_neg,curr_exp,exponent)=(%d,%d,%d)\n",
is_neg, curr_exp, exponent);
#endif
assert(exponent > curr_exp);
while (curr_exp != exponent){
//shift right
shift_right(acc);
curr_exp++;
}
//curr_exp does NOT get written back to acc->exponent
}
//for internal use only,
//AccDecn must be larger than BDecn in absolute value
//subtract by equal addition algorithm
static void sub_mag(){
int8_t i;
uint8_t carry = 0;
//normalize
remove_leading_zeros(&AccDecn);
remove_leading_zeros(&BDecn);
if (get_exponent(&AccDecn) != get_exponent(&BDecn)){
_incr_exp(&BDecn, get_exponent(&AccDecn));
}
#ifdef DEBUG_ADD
decn_to_str_complete(&tmp);
printf(" incr_exp tmp: %s\n", Buf);
#endif
//do subtraction
for (i = DEC80_NUM_LSU - 1; i >=0; i--){
uint8_t digit100;
if (AccDecn.lsu[i] >= (BDecn.lsu[i] + carry)){
digit100 = AccDecn.lsu[i] - (BDecn.lsu[i] + carry);
carry = 0;
} else {
digit100 = AccDecn.lsu[i] + 100 - (BDecn.lsu[i] + carry);
carry = 1;
}
assert(digit100 < 100);
AccDecn.lsu[i] = digit100;
}
assert(carry == 0); //shouldn't be carry out if |acc| > |x|
}
void add_decn(void){
int8_t rel;
uint8_t carry = 0;
int8_t i;
//check if zero
if (decn_is_zero(&BDecn)){
return;
} else if (decn_is_zero(&AccDecn)){
copy_decn(&AccDecn, &BDecn);
return;
}
//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);
//handle cases where signs differ
if (AccDecn.exponent < 0 && BDecn.exponent >= 0){
// -acc, +x
rel = compare_magn();
if (rel == 1){
#ifdef DEBUG_ADD
printf("|-acc| > |+x|\n");
#endif
sub_mag();
} else if (rel == -1){
#ifdef DEBUG_ADD
printf("|-acc| < |+x|\n");
#endif
copy_decn(&BDecn, &AccDecn);
copy_decn(&AccDecn, &TmpDecn);
sub_mag();
} else { //equal
#ifdef DEBUG_ADD
printf("|-acc| == |+x|\n");
#endif
set_dec80_zero(&AccDecn);
}
//restore b
copy_decn(&BDecn, &TmpDecn);
return;
} else if (AccDecn.exponent >= 0 && BDecn.exponent < 0){
// +acc, -x
rel = compare_magn();
if (rel == 1){
#ifdef DEBUG_ADD
printf("|+acc| > |-x|\n");
#endif
sub_mag();
} else if (rel == -1){
#ifdef DEBUG_ADD
printf("|+acc| < |-x|\n");
#endif
copy_decn(&BDecn, &AccDecn);
copy_decn(&AccDecn, &TmpDecn);
sub_mag();
} else { //equal
#ifdef DEBUG_ADD
printf("|+acc| == |-x|\n");
#endif
set_dec80_zero(&AccDecn);
}
//restore b
copy_decn(&BDecn, &TmpDecn);
return;
}
//signs must now be the same, begin adding
//normalize
remove_leading_zeros(&AccDecn);
remove_leading_zeros(&BDecn);
#ifdef DEBUG_ADD
decn_to_str_complete(&AccDecn);
printf(" rem_leading_zeros acc: %s\n", Buf);
decn_to_str_complete(&BDecn);
printf(" rem_leading_zeros tmp: %s\n", Buf);
#endif
if (get_exponent(&AccDecn) > get_exponent(&BDecn)){
_incr_exp(&BDecn, get_exponent(&AccDecn));
} else if (get_exponent(&AccDecn) < get_exponent(&BDecn)){
//shift significand and adjust exponent to match
for (i = 0; i < get_exponent(&BDecn) - get_exponent(&AccDecn); i++){
shift_right(&AccDecn);
}
set_exponent(&AccDecn, get_exponent(&BDecn), (AccDecn.exponent < 0));
}
#ifdef DEBUG_ADD
decn_to_str_complete(&AccDecn);
printf(" incr_exp acc: %s\n", Buf);
decn_to_str_complete(&BDecn);
printf(" incr_exp tmp: %s\n", Buf);
#endif
//do addition
for (i = DEC80_NUM_LSU - 1; i >= 0; i--){
uint8_t digit100 = AccDecn.lsu[i] + BDecn.lsu[i] + carry;
AccDecn.lsu[i] = digit100 % 100;
carry = digit100 / 100;
assert(carry <= 1);
}
//may need to rescale number
if (carry > 0){
exp_t curr_exp = get_exponent(&AccDecn);
assert(carry == 1);
rel = (AccDecn.exponent < 0); //is_neg?
//shift right
shift_right(&AccDecn);
AccDecn.lsu[0] += 10; //carry gets shifted into most significant digit
curr_exp++;
//track sign
set_exponent(&AccDecn, curr_exp, rel); //rel==is_neg?
}
//restore b
copy_decn(&BDecn, &TmpDecn);
}
//AccDecn *= BDecn
// (the BDecn register is preserved)
void mult_decn(void){
int8_t i, j;
uint8_t carry = 0;
uint8_t is_neg;
exp_t new_exponent;
#ifdef EXTRA_CHECKS
if (decn_is_nan(&AccDecn) || decn_is_nan(&BDecn)) {
set_dec80_NaN(&AccDecn);
return;
}
#endif
//initialize values
set_dec80_zero(&TmpDecn);
//normalize
remove_leading_zeros(&AccDecn);
remove_leading_zeros(&BDecn);
//store new sign
#ifdef EXP16
if ((AccDecn.exponent & 0x8000) ^ (BDecn.exponent & 0x8000)){ //signs differ
#else
if ((AccDecn.exponent & 0x80) ^ (BDecn.exponent & 0x80)){ //signs differ
#endif
is_neg = 1;
} else {
is_neg = 0;
}
//calculate new exponent
new_exponent = get_exponent(&AccDecn) + get_exponent(&BDecn);
#ifdef DEBUG_MULT
printf("\n a_exp: %d, b_exp: %d", get_exponent(&AccDecn), get_exponent(&BDecn));
printf("\n new exponent: %d, is_neg: %u", new_exponent, is_neg);
#endif
//do multiply
for (i = DEC80_NUM_LSU - 1; i >= 0; i--){
//partial product
for (j = DEC80_NUM_LSU - 1; j >= 0; j--){
uint16_t digit100 = TmpDecn.lsu[j] + (BDecn.lsu[i] * AccDecn.lsu[j]) + carry;
TmpDecn.lsu[j] = digit100 % 100;
carry = digit100 / 100;
assert(carry < 100);
}
#ifdef DEBUG_MULT_ALL
printf("\n%d:", i);
printf("\n acc:");
for (j = 0; j < DEC80_NUM_LSU; j++){
printf(" %3d", AccDecn.lsu[j]);
}
printf("\n x:");
for (j = 0; j < DEC80_NUM_LSU; j++){
if (j == i)
printf(" %3d", BDecn.lsu[j]);
else
printf(" ");
}
#endif
#ifdef DEBUG_MULT
printf("\n TmpDecn:");
for (j = 0; j < DEC80_NUM_LSU; j++){
printf(" %3d", TmpDecn.lsu[j]);
}
printf("\ncarry:%d", carry);
#endif
if (i != 0){ //handle last carry separately later, no final shift
//shift
shift_right(&TmpDecn);
shift_right(&TmpDecn);
//add back carry to MSdigit100
TmpDecn.lsu[0] = carry; //was 0 from shift
}
}
//handle last carry
if (carry >= 10){
//shift
shift_right(&TmpDecn);
shift_right(&TmpDecn);
new_exponent += 1;
//add back carry to MSdigit100
TmpDecn.lsu[0] = carry; //was 0 from shift
} else if (carry > 0){
//shift
shift_right(&TmpDecn);
//add back carry to MSdigit in MSdigit100
TmpDecn.lsu[0] += carry*10;
}
//set new exponent, checking for over/underflow
#ifdef DEBUG_MULT
printf("\n new exponent: %d, is_neg: %u\n", new_exponent, is_neg);
#endif
if (new_exponent < DEC80_MAX_EXP && new_exponent > DEC80_MIN_EXP){
set_exponent(&TmpDecn, new_exponent, is_neg);
} else {
set_dec80_NaN(&AccDecn);
return;
}
//copy back to acc
copy_decn(&AccDecn, &TmpDecn);
//normalize
remove_leading_zeros(&AccDecn);
}
void recip_decn(void){
#define CURR_RECIP Tmp2Decn //copy of x, holds current 1/x estimate
uint8_t i;
exp_t initial_exp;
//check divide by zero
#ifdef EXTRA_CHECKS
if (decn_is_zero(&AccDecn)){
set_dec80_NaN(&AccDecn);
#ifdef DESKTOP
printf("error division by 0\n");
#endif
return;
}
#endif
//normalize
remove_leading_zeros(&AccDecn);
//store copy of x
st_push_decn(&AccDecn);
//get initial exponent of estimate for 1/x
initial_exp = get_exponent(&AccDecn);
#ifdef DEBUG_DIV
printf("exponent %d", initial_exp);
#endif
//necessary to subtract 1 for convergence
initial_exp = -initial_exp - 1;
#ifdef DEBUG_DIV
printf(" -> %d\n", initial_exp);
#endif
set_exponent(&CURR_RECIP, initial_exp, (AccDecn.exponent < 0)); //set exponent, copy sign
//get initial estimate for 1/x
if (AccDecn.lsu[0] < 20){ //mantissa between 1 and 2
CURR_RECIP.lsu[0] = 50; //0.50 with implicit point and exponent
} else if (AccDecn.lsu[0] < 33){
CURR_RECIP.lsu[0] = 30;
} else if (AccDecn.lsu[0] < 50){
CURR_RECIP.lsu[0] = 20;
} else {
CURR_RECIP.lsu[0] = 10; //0.1 with implicit point and exponent
}
for (i = 1; i < DEC80_NUM_LSU; i++){
CURR_RECIP.lsu[i] = 0;
}
copy_decn(&AccDecn, &CURR_RECIP);
//do newton-raphson iterations
for (i = 0; i < 6; i++){ //just fix number of iterations for now
#ifdef DEBUG_DIV
decn_to_str_complete(&CURR_RECIP);
printf("%2d: %s\n", i, Buf);
#endif
//Accum *= x_copy
st_load_decn(&BDecn);
mult_decn();
#ifdef DEBUG_DIV
decn_to_str_complete(&AccDecn);
printf(" %20s: %s\n", "recip*x", Buf);
#endif
//Accum *= -1
negate_decn(&AccDecn);
//Accum += 1
copy_decn(&BDecn, &DECN_1);
add_decn();
#ifdef DEBUG_DIV
decn_to_str_complete(&AccDecn);
printf(" %20s: %s\n", "(1-recip*x)", Buf);
#endif
//Accum *= curr_recip
copy_decn(&BDecn, &CURR_RECIP);
mult_decn();
#ifdef DEBUG_DIV
decn_to_str_complete(&AccDecn);
printf(" %20s: %s\n", "recip * (1-recip*x)", Buf);
#endif
//Accum += curr_recip
add_decn();
//new_est(Accum) = recip + (1 - recip*x)*recip, where recip is current recip estimate
copy_decn(&CURR_RECIP, &AccDecn);
}
st_pop_decn(0);
//try not to pollute namespace
#undef CURR_RECIP
}
void div_decn(void){
//store copy of acc for final multiply by 1/x
st_push_decn(&AccDecn);
copy_decn(&AccDecn, &BDecn);
recip_decn();
//Accum now holds 1/x, multiply by original acc to complete division
st_pop_decn(&BDecn);
mult_decn();
}
//constants used for ln(x) and exp(x)
#define NUM_A_ARR 9
static const dec80 LN_A_ARR[NUM_A_ARR] = {
{-1 & 0x7fff, {69, 31, 47, 18, 5, 59, 94, 53, 9}},
{-2 & 0x7fff, {95, 31, 1, 79, 80, 43, 24, 86, 0}},
{-3 & 0x7fff, {99, 50, 33, 8, 53, 16, 80, 82, 84}},
{-4 & 0x7fff, {99, 95, 0, 33, 30, 83, 53, 31, 67}},
{-5 & 0x7fff, {99, 99, 50, 0, 33, 33, 8, 33, 33}},
{-6 & 0x7fff, {99, 99, 95, 0, 0, 33, 33, 29, 95}},
{-7 & 0x7fff, {99, 99, 99, 50, 0, 0, 33, 5, 35}},
{-8 & 0x7fff, {99, 99, 99, 95, 0, 0, 2, 76, 40}},
{-9 & 0x7fff, {99, 99, 99, 99, 50, 0, 15, 98, 65}},
};
void ln_decn(void){
uint8_t j, k;
#define B_j Tmp2Decn
#define NUM_TIMES Tmp3Decn
//check not negative or zero
if (AccDecn.exponent < 0 || decn_is_zero(&AccDecn)){
set_dec80_NaN(&AccDecn);
return;
}
//normalize
remove_leading_zeros(&AccDecn);
//scale to between 1 and 10
NUM_TIMES.exponent = get_exponent(&AccDecn) + 1; //store initial exp in NUM_TIMES.exponent
AccDecn.exponent = 0;
#ifdef DEBUG_LOG
decn_to_str_complete(&AccDecn);
printf("ln() accum scaled between 1,10: %s\n", Buf);
#endif
//get initial estimate (accum = 10 - A)
copy_decn(&BDecn, &DECN_1);
BDecn.exponent = 1; //BDecn = 10
negate_decn(&AccDecn);
add_decn();
copy_decn(&B_j, &AccDecn); //b_j = accum = 10 - A
#ifdef DEBUG_LOG
decn_to_str_complete(&AccDecn);
printf("ln() initial accum: %s (%d)\n", Buf, NUM_TIMES.exponent);
#endif
//track number of times multiplied by a_arr[j]
for (j = 0; j < NUM_A_ARR; j++){
uint8_t k_j;
if (j != 0){
//b_j *= 10.0
shift_left(&B_j);
}
copy_decn(&AccDecn, &B_j); //accum = b_j
k_j = 0;
while (!(AccDecn.exponent < 0)){ //while >= 0 (!negative) TODO: should just be >
uint8_t i;
copy_decn(&B_j, &AccDecn); //b_j = accum
//accum *= a_arr[j]
// since a_arr[j] is of the form 1 + 10^-j, just shift accum and add to self
copy_decn(&BDecn, &AccDecn);
for (i = 0; i < j; i++){
shift_right(&BDecn);
}
add_decn();
//accum -= 10
// accum.exponent is 1 while needs subtracting
if (AccDecn.lsu[0] >= 10 && get_exponent(&AccDecn) > 0){
AccDecn.lsu[0] -= 10;
} else {
//set as negative to get out of while(), accum will get overwritten with b_j
AccDecn.exponent = -1;
}
#ifdef DEBUG_LOG_ALL
decn_to_str_complete(&AccDecn);
printf(" %u: %s\t", k_j, Buf);
for (int ii = 0; ii < DEC80_NUM_LSU; ii++){
printf("%2d ", AccDecn.lsu[ii]);
}
printf(" (%d)\n", get_exponent(&AccDecn));
#endif
k_j++;
#ifdef DEBUG_LOG
assert(k_j != 255);
#endif
}
//track num times
NUM_TIMES.lsu[j] = k_j - 1;
#ifdef DEBUG_LOG
decn_to_str_complete(&B_j);
printf(" %u: num_times: %u, %s\n", j, NUM_TIMES.lsu[j], Buf);
#endif
}
//build final value
copy_decn(&AccDecn, &B_j); //remainder
#ifdef DEBUG_LOG
decn_to_str_complete(&AccDecn);
printf("ln() remainder: %s\n", Buf);
#endif
for (j = NUM_A_ARR - 1; j < NUM_A_ARR; j--){ //sum in reverse order, note: (j < NUM_A_ARR) == signed(j >= 0)
for (k = 0; k < NUM_TIMES.lsu[j]; k++){
//accum += ln_a_arr[j];
copy_decn(&BDecn, &LN_A_ARR[j]);
BDecn.exponent = 0; //tracking exponent through shifts/saved initial exponent
add_decn();
}
shift_right(&AccDecn);
#ifdef DEBUG_LOG
decn_to_str_complete(&AccDecn);
printf("%u: ln() remainder: %s\n", j, Buf);
#endif
}
#ifdef DEBUG_LOG
decn_to_str_complete(&AccDecn);
printf("ln() accum after summing: %s\n", Buf);
#endif
//accum = -accum;
negate_decn(&AccDecn);
//add back in initial exponent (stored in NUM_TIMES.exponent)
copy_decn(&B_j, &AccDecn); //temporarily store accum in B_j
set_dec80_zero(&AccDecn);
//check if negative
if (NUM_TIMES.exponent < 0){
j = 1; //track is_neg
NUM_TIMES.exponent = -NUM_TIMES.exponent;
} else {
j = 0;
}
//check if too big for single lsu
#ifdef EXP16
if (NUM_TIMES.exponent >= 10000){
AccDecn.lsu[0] = NUM_TIMES.exponent / 10000;
NUM_TIMES.exponent = NUM_TIMES.exponent % 10000;
AccDecn.lsu[1] = NUM_TIMES.exponent / 100;
AccDecn.lsu[2] = NUM_TIMES.exponent % 100;
AccDecn.exponent = 5;
} else
#endif
if (NUM_TIMES.exponent >= 100){
AccDecn.lsu[0] = NUM_TIMES.exponent / 100;
AccDecn.lsu[1] = NUM_TIMES.exponent % 100;
AccDecn.exponent = 3;
} else {
AccDecn.lsu[0] = NUM_TIMES.exponent;
AccDecn.exponent = 1;
}
//check if need to negate
if (j) { //was negative
negate_decn(&AccDecn);
}
#ifdef DEBUG_LOG
decn_to_str_complete(&AccDecn);
printf("ln() exponent from initial: %s\n", Buf);
#endif
//initial exp * ln(10)
copy_decn(&BDecn, &DECN_LN_10);
mult_decn();
//add back stored accum
copy_decn(&BDecn, &B_j);
add_decn();
//try not to pollute namespace
#undef B_j
#undef NUM_TIMES
}
void log10_decn(void){
ln_decn();
copy_decn(&BDecn, &DECN_LN_10);
div_decn();
}
void exp_decn(void){
uint8_t j, k;
uint8_t need_recip = 0;
#define SAVED Tmp2Decn
#define NUM_TIMES Tmp3Decn
//check not error
if (decn_is_nan(&AccDecn)){
set_dec80_NaN(&AccDecn);
return;
}
//check if negative
if (AccDecn.exponent < 0){
negate_decn(&AccDecn);
need_recip = 1;
}
//check if in range
copy_decn(&SAVED, &AccDecn); //save = accum
set_dec80_zero(&BDecn);
BDecn.lsu[0] = 29;
BDecn.lsu[1] = 47;
BDecn.exponent = 2; //b = 294.7
negate_decn(&BDecn);
add_decn(); //accum = x - 294.7 (should be negative if in range)
if (!(AccDecn.exponent < 0)){ //if not negative
set_dec80_NaN(&AccDecn);
return;
}
copy_decn(&AccDecn, &SAVED); //restore
// //normalize
// remove_leading_zeros(&AccDecn);
//initial b = -10*ln(10)
copy_decn(&BDecn, &DECN_LN_10); //b=ln(10)
copy_decn(&SAVED, &AccDecn); //save = accum
copy_decn(&AccDecn, &DECN_1);
AccDecn.exponent = 1; //accum = 10
mult_decn(); //accum = 10*ln(10)
copy_decn(&BDecn, &AccDecn); //b = 10*ln(10)
negate_decn(&BDecn); //b = -10*ln(10)
copy_decn(&AccDecn, &SAVED); //restore
//track number of times 10*ln(10) can be subtracted
k = 0;
while (!(AccDecn.exponent < 0)){ //while not negative
copy_decn(&SAVED, &AccDecn); //save = accum
//accum -= 10*ln10
add_decn();
k++;
}
//subtracted 1 time too many
NUM_TIMES.exponent = (k - 1) * 10; //use exp to store number of times ln(10) subtracted
copy_decn(&AccDecn, &SAVED); //restore
#ifdef DEBUG_EXP
decn_to_str_complete(&AccDecn);
printf("exp() num_times for 10*ln(10): %s (%d)\n", Buf, NUM_TIMES.exponent);
#endif
//load b with -ln(10)
copy_decn(&BDecn, &DECN_LN_10); //b = ln(10)
negate_decn(&BDecn); //b = -ln(10)
//track number of times ln(10) and then (1 + 10^-j) can be subtracted
j = UINT8_MAX; //becomes 0 after incrementing to start (1 + 10^-j) series
do {
k = 0;
while (!(AccDecn.exponent < 0)){ //while not negative
copy_decn(&SAVED, &AccDecn); //save = accum
//accum -= b (ln10, then ln(1 + 10^-j) series)
add_decn();
#ifdef DEBUG_EXP_ALL
decn_to_str_complete(&AccDecn);
printf(" %u: %s\n", k, Buf);
// decn_to_str_complete(&BDecn);
// printf("(%s)\n", Buf);
#endif
k++;
#ifdef DEBUG_EXP
assert(k != 255);
#endif
}
//subtracted 1 time too many:
if (j == UINT8_MAX){
NUM_TIMES.exponent += k - 1;
} else {
NUM_TIMES.lsu[j] = k - 1;
}
copy_decn(&AccDecn, &SAVED); //restore
#ifdef DEBUG_EXP
decn_to_str_complete(&AccDecn);
printf("exp() num_times for %d: %s (%d)\n", j, Buf, k-1);
#endif
//next j
j++;
if (j < NUM_A_ARR){
//get next ln(1 + 10^-j) for subtraction
copy_decn(&BDecn, &LN_A_ARR[j]);
negate_decn(&BDecn);
} else {
break;
}
} while (1);
//build final value
// (currently accum = save = remainder)
// calculate 1+remainder
copy_decn(&BDecn, &DECN_1);
add_decn();
//get initial multiplier (10) for ln(10)
copy_decn(&BDecn, &DECN_1);
BDecn.exponent = 1; //BDecn = 10
//do multiplies
j = UINT8_MAX; //becomes 0 after incrementing to start (1 + 10^-j) series
do {
for (k = 0; k < (j==UINT8_MAX ? NUM_TIMES.exponent : NUM_TIMES.lsu[j]); k++){
mult_decn();
}
#ifdef DEBUG_EXP
decn_to_str_complete(&AccDecn);
printf("exp() current val for %d: %s\n", j, Buf);
#endif
//next j
j++;
if (j < NUM_A_ARR){
//get next multiplier (1 + 10^-j) for ln(1 + 10^-j)
if (j == 0){
//set to 2
BDecn.lsu[0] = 20;
BDecn.exponent = 0;
} else if (j == 1) {
//set to 1.1
BDecn.lsu[0] = 11;
//exponent is already 0
} else {
//get next (1 + 10^-j)
shift_right(&BDecn);
BDecn.lsu[0] = 10;
}
} else {
break;
}
} while (1);
#ifdef DEBUG_EXP
decn_to_str_complete(&AccDecn);
printf("exp() before recip: %s\n", Buf);
#endif
//take reciprocal if exp was negative
if (need_recip){
recip_decn();
}
#ifdef DEBUG_EXP
decn_to_str_complete(&AccDecn);
printf("exp() final val: %s\n", Buf);
#endif
//try not to pollute namespace
#undef SAVED
#undef NUM_TIMES
}
void exp10_decn(void){
//exp10_decn() = exp_decn(AccDecn * ln(10))
copy_decn(&BDecn, &DECN_LN_10);
mult_decn();
exp_decn();
}
void pow_decn(void) {
if (decn_is_zero(&BDecn)) {
copy_decn(&AccDecn, &DECN_1);
return;
}
if (decn_is_zero(&AccDecn)) {
set_dec80_zero(&AccDecn);
return;
}
//calculate AccDecn = AccDecn ^ BDecn
st_push_decn(&BDecn);
ln_decn();
st_pop_decn(&BDecn);
mult_decn(); //accum = b*ln(accum)
exp_decn();
}
#ifdef USE_POW_SQRT_IMPL
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);
}
#else
void sqrt_decn(void){
#define CURR_EST Tmp2Decn //holds current 1/sqrt(x) estimate
#define X_2 Tmp3Decn //holds copy of original x / 2
uint8_t i;
exp_t initial_exp;
if (decn_is_nan(&AccDecn)) {
return;
}
if (AccDecn.exponent < 0){ //negative
set_dec80_NaN(&AccDecn);
return;
}
//normalize
remove_leading_zeros(&AccDecn);
#ifdef DEBUG_SQRT
decn_to_str_complete(&AccDecn);
printf("sqrt in: %s\n", Buf);
#endif
//store copy of x
st_push_decn(&AccDecn);
//calculate x_orig / 2
set_dec80_zero(&BDecn);
BDecn.lsu[0] = 5;
mult_decn();
copy_decn(&X_2, &AccDecn);
//restore x
st_load_decn(&AccDecn);
//get initial estimate for 1/sqrt(x) == 10^(-0.5 * log(x)):
// approximate significand == 10^(-0.5 * log(x_signif))
// with linear approximation: -0.18 * x_signif + 2.5
// new exponent part is (10^(-0.5 * log(10^x_exp)))
// == 10^(-0.5 * x^exp)
initial_exp = get_exponent(&AccDecn);
set_exponent(&AccDecn, 0, 0); //clear exponent (Acc is not negative)
#ifdef DEBUG_SQRT
printf("sqrt exponent %d ", initial_exp);
#endif
if (initial_exp & 0x1){ //odd
#ifdef DEBUG_SQRT
printf("(odd) ");
#endif
//increment x_exp and
initial_exp++;
//approximate estimated significand as (-0.056*x_signif + 0.79) * 10^0.5
// == -0.18 * x_signif + 2.5
//b = -0.18
BDecn.lsu[0] = 18;
BDecn.exponent = -1; //negative, and exponent = -1
//a = -0.18 * x_signif
mult_decn();
//b = 2.5
BDecn.lsu[0] = 25;
BDecn.exponent = 0;
//a = -0.18 * x_signif + 2.5
add_decn();
} else { //even
//keep x_exp as is and approximate estimated significand as
// -0.056*x_signif + 0.79
//b = -0.056
BDecn.lsu[0] = 56;
set_exponent(&BDecn, -2, 1);
//a = -0.056 * x_signif
mult_decn();
//b = 0.79
BDecn.lsu[0] = 7;
BDecn.lsu[1] = 90;
BDecn.exponent = 0;
//a = -0.056*x_signif + 0.79
add_decn();
}
//est_exp = -x_exp / 2;
initial_exp = -initial_exp / 2;
//est_exp-- if AccDecn exponent is negative
// (AccDecn exponent is either 0 or -1, and AccDecn is positive)
if (AccDecn.exponent != 0){
initial_exp--;
}
set_exponent(&AccDecn, initial_exp, 0); //(initial estimate is never negative)
copy_decn(&CURR_EST, &AccDecn);
#ifdef DEBUG_SQRT
printf(" -> %d\n", initial_exp);
#endif
//do newton-raphson iterations
for (i = 0; i < 6; i++){ //just fix number of iterations for now
#ifdef DEBUG_SQRT
decn_to_str_complete(&CURR_EST);
printf("sqrt %2d: %s\n", i, Buf);
#endif
//accum = est * est;
copy_decn(&BDecn, &AccDecn);
mult_decn();
//accum *= x_orig_2; //accum = x/2 * est * est
copy_decn(&BDecn, &X_2);
mult_decn();
//accum = - x/2 * est * est
negate_decn(&AccDecn);
//b = 3/2
set_dec80_zero(&BDecn);
BDecn.lsu[0] = 15;
//accum = 3/2 - x/2 * est * est
add_decn();
//accum *= est; //accum = 0.5 * est * (3 - x * est * est)
copy_decn(&BDecn, &CURR_EST);
mult_decn();
//est = accum;
copy_decn(&CURR_EST, &AccDecn);
}
//calc sqrt from recip_sqrt
st_pop_decn(&BDecn);
mult_decn();
#undef CURR_EST
#undef X_COPY
}
#endif //USE_POW_SQRT_IMPL
// see W.E. Egbert, "Personal Calculator Algorithms II: Trigonometric functions"
void project_decn_into_0_2pi(void) {
const uint8_t is_negative = (AccDecn.exponent < 0);
exp_t exponent;
remove_leading_zeros(&AccDecn);
if (is_negative) {
negate_decn(&AccDecn);
}
exponent = get_exponent(&AccDecn);
copy_decn(&BDecn, &DECN_2PI);
if (compare_magn() > 0) {
do {
do {
copy_decn(&BDecn, &DECN_2PI);
BDecn.exponent = exponent;
if (compare_magn() >= 0) {
negate_decn(&BDecn);
add_decn();
} else {
break;
}
} while (1);
exponent--;
} while (exponent >= 0);
}
if (is_negative) {
negate_decn(&AccDecn);
copy_decn(&BDecn, &DECN_2PI);
add_decn();
}
}
// K. Shirriff, "Reversing Sinclair's amazing 1974 calculator hack - half the ROM of the HP-35"
// http://files.righto.com/calculator/sinclair_scientific_simulator.html
#define SIN Tmp2Decn
#define COS Tmp3Decn
#define THETA Tmp4Decn
void sincos_decn(const uint8_t sincos_arctan) {
const uint8_t is_negative = AccDecn.exponent < 0;
if (sincos_arctan) {
set_dec80_zero(&THETA);
if (is_negative) negate_decn(&AccDecn);
copy_decn(&COS, &AccDecn);
copy_decn(&SIN, &DECN_1);
} else {
project_decn_into_0_2pi();
copy_decn(&THETA, &AccDecn);
copy_decn(&COS, &DECN_1);
set_dec80_zero(&SIN);
// 0.0 00 5
SIN.lsu[2] = 50;
negate_decn(&SIN);
}
do {
if (sincos_arctan) {
// THETA is in AccDecn from previous iteration
if (COS.exponent < 0) {
if (is_negative) negate_decn(&AccDecn);
break;
}
} else {
if (THETA.exponent < 0) {
break;
}
}
// COS = COS - SIN / 1000
copy_decn(&AccDecn, &COS);
copy_decn(&BDecn, &SIN);
shift_right(&BDecn);
shift_right(&BDecn);
shift_right(&BDecn);
negate_decn(&BDecn);
add_decn();
copy_decn(&COS, &AccDecn);
// SIN = SIN + COS / 1000
copy_decn(&AccDecn, &SIN);
copy_decn(&BDecn, &COS);
shift_right(&BDecn);
shift_right(&BDecn);
shift_right(&BDecn);
add_decn();
copy_decn(&SIN, &AccDecn);
// THETA = THETA -/+ 0.0 01
copy_decn(&AccDecn, &THETA);
set_dec80_zero(&BDecn);
BDecn.lsu[1] = 1;
if (!sincos_arctan) negate_decn(&BDecn);
add_decn();
copy_decn(&THETA, &AccDecn);
} while (1);
}
void sin_decn(void) {
sincos_decn(0);
copy_decn(&AccDecn, &SIN);
}
void cos_decn(void) {
sincos_decn(0);
copy_decn(&AccDecn, &COS);
}
void tan_decn(void) {
sincos_decn(0);
copy_decn(&AccDecn, &SIN);
copy_decn(&BDecn, &COS);
div_decn();
}
void arctan_decn(void) {
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 COS
#undef THETA
void to_degree_decn(void) {
copy_decn(&BDecn, &DECN_1RAD);
mult_decn();
}
void to_radian_decn(void) {
copy_decn(&BDecn, &DECN_1RAD);
div_decn();
}
void pi_decn(void) {
set_dec80_zero(&BDecn);
BDecn.lsu[0] = 5; // 0.5 00 ..
copy_decn(&AccDecn, &DECN_2PI);
mult_decn();
}
static void set_str_error(void){
Buf[0] = 'E';
Buf[1] = 'r';
Buf[2] = 'r';
Buf[3] = 'o';
Buf[4] = 'r';
Buf[5] = '\0';
}
#ifdef DESKTOP
int
#else
int8_t
#endif
decn_to_str(const dec80* x){
#define INSERT_DOT() Buf[i++]='.'
uint8_t i = 0;
uint8_t digit100;
exp_t exponent = 0;
uint8_t trailing_zeros = 0;
uint8_t use_sci = 0;
static __xdata dec80 tmp;
//handle corner case of NaN
if (decn_is_nan(x)){
set_str_error();
#ifdef DEBUG
printf (" corner case NaN ");
#endif
return 0;
}
//copy and normalize
copy_decn(&tmp, x);
remove_leading_zeros(&tmp);
//handle corner case of 0
if (tmp.lsu[0] == 0){
#ifdef DEBUG
printf (" corner case, set to 0 ");
#endif
Buf[0] = '0';
Buf[1] = '\0';
return 0;
}
//check sign of number
if (tmp.exponent < 0){
#ifdef DEBUG
printf (" negative ");
#endif
Buf[i] = '-';
i++;
}
//check if we should use scientific notation
exponent = get_exponent(&tmp);
if (exponent > (num_digits_display - 1) || exponent < -3){
use_sci = 1;
}
//pad zeros right of decimal point if needed
if (!use_sci && exponent < 0){
exp_t j;
Buf[i] = '0';
i++;
INSERT_DOT();
//pad zeros right of decimal point
// for (j = exponent + 1; j < 0; j++){ <--- results in undefined behavior (signed overflow), and causes crash
for (j = -exponent -1; j > 0; --j){
Buf[i] = '0';
i++;
}
}
//print 1st digit
Buf[i] = (tmp.lsu[0] / 10) + '0';
i++;
if (use_sci) {
INSERT_DOT();
} else {
if (exponent == 0){
INSERT_DOT();
}
exponent--;
}
//print 2nd digit
Buf[i] = (tmp.lsu[0] % 10) + '0';
if (tmp.lsu[0] % 10 == 0 && (use_sci || exponent < 0)){
trailing_zeros = 1;
}
i++;
if (!use_sci){
if (exponent == 0){
INSERT_DOT();
}
exponent--;
}
//print rest of significand
for (digit100 = 1 ; digit100 < num_digits_display/2; digit100++){
//print 1st digit
Buf[i] = (tmp.lsu[digit100] / 10) + '0';
i++;
if (!use_sci){
if (exponent == 0){
INSERT_DOT();
}
exponent--;
}
//print 2nd digit
Buf[i] = (tmp.lsu[digit100] % 10) + '0';
i++;
if (!use_sci){
if (exponent == 0){
INSERT_DOT();
}
exponent--;
}
//track trailing 0s
if (tmp.lsu[digit100] == 0 && (use_sci || exponent < 0)){
if (use_sci || exponent < -2){ //xx.00
trailing_zeros += 2;
} else if (exponent == -2){ //xx.0
trailing_zeros += 1;
}
} else if (tmp.lsu[digit100]%10 == 0 && (use_sci || exponent < 0)){
trailing_zeros = 1;
} else {
trailing_zeros = 0;
}
}
//remove trailing zeros
if (use_sci || exponent <= 0){
i -= trailing_zeros;
}
Buf[i] = '\0';
//calculate exponent
exponent = get_exponent(&tmp); //base 100
#ifdef DEBUG
printf (" exponent (%d)", exponent);
#endif
//print exponent
if (use_sci){
//check for overflow
#ifdef DESKTOP
if (exponent > DEC80_MAX_EXP || exponent < DEC80_MIN_EXP){
#else
if (exponent > DECN_MAX_PRINT_EXP || exponent < DECN_MIN_PRINT_EXP){
#endif
set_str_error();
return 0;
}
return exponent;
} else {
return 0;
}
#ifdef DEBUG
printf (" final i (%d) ", i);
for (int jjj = 0; jjj < DEC80_NUM_LSU; jjj++){
printf(" %02d", tmp.lsu[jjj]);
}
printf("\n");
#endif
}
#ifdef DESKTOP
//complete string including exponent
void decn_to_str_complete(const dec80* x){
int exponent = decn_to_str(x);
int i;
//find end of string
for (i = 0; Buf[i] != '\0'; i++);
//add exponent
if (exponent != 0){
Buf[i++] = 'E';
if (exponent < 0){
Buf[i++] = '-';
u32str(-exponent, &Buf[i], 10); //adds null terminator automatically
} else {
u32str(exponent, &Buf[i], 10); //adds null terminator automatically
}
}
}
void build_decn_at(dec80* dest, const char* signif_str, exp_t exponent){
dec80 tmp;
copy_decn(&tmp, &AccDecn); //save
build_dec80(signif_str, exponent);
copy_decn(dest, &AccDecn);
copy_decn(&AccDecn, &tmp); //restore
}
#endif //DESKTOP