initial decimal number code, needs work
- need to change to have implicit decimal point after first digit - need to rework for code size
This commit is contained in:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							@ -3,7 +3,7 @@ STCCODESIZE ?= 13000
 | 
			
		||||
SDCCOPTS ?= --code-size $(STCCODESIZE) --xram-size 0 --stack-auto
 | 
			
		||||
FLASHFILE ?= main.hex
 | 
			
		||||
 | 
			
		||||
SRC = src/lcd.c src/key.c src/utils.c
 | 
			
		||||
SRC = src/lcd.c src/key.c src/utils.c src/decn/decn.c
 | 
			
		||||
 | 
			
		||||
OBJ=$(patsubst src%.c,build%.rel, $(SRC))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										658
									
								
								src/decn/decn.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										658
									
								
								src/decn/decn.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,658 @@
 | 
			
		||||
/*
 | 
			
		||||
 * decn.c
 | 
			
		||||
 *
 | 
			
		||||
 *  Created on: Mar 21, 2019
 | 
			
		||||
 *      Author: jeffrey
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "../utils.h"
 | 
			
		||||
 | 
			
		||||
//#define DEBUG
 | 
			
		||||
#define DEBUG_ADD
 | 
			
		||||
 | 
			
		||||
#ifndef DESKTOP
 | 
			
		||||
#undef DEBUG
 | 
			
		||||
#undef DEBUG_ADD
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef DESKTOP
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef DESKTOP
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#else
 | 
			
		||||
#define assert(x)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "decn.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void dec64to80(dec80* dest, const dec64* src){
 | 
			
		||||
	uint8_t i;
 | 
			
		||||
	dest->exponent = src->exponent;
 | 
			
		||||
 | 
			
		||||
	//clear extra nibbles
 | 
			
		||||
	for (i = DEC80_NUM_LSU - 1; i >= DEC64_NUM_LSU; i--){
 | 
			
		||||
		dest->lsu[i] = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//copy nibbles
 | 
			
		||||
	for (i = 0; i < DEC64_NUM_LSU; i++){
 | 
			
		||||
		dest->lsu[i] = src->lsu[i];
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dec80to64(dec64* dest, const dec80* src){
 | 
			
		||||
	//TODO:
 | 
			
		||||
	assert(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void copy_decn(dec80* dest, const dec80* src){
 | 
			
		||||
	uint8_t i;
 | 
			
		||||
	dest->exponent = src->exponent;
 | 
			
		||||
 | 
			
		||||
	//copy nibbles
 | 
			
		||||
	for (i = 0; i < DEC80_NUM_LSU; i++){
 | 
			
		||||
		dest->lsu[i] = src->lsu[i];
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int16_t get_exponent(const dec80* x){
 | 
			
		||||
	int16_t exponent = x->exponent;
 | 
			
		||||
	if (exponent & 0x4000){ //negative
 | 
			
		||||
		return (uint16_t) exponent | 0x8000;
 | 
			
		||||
	} else { //positive
 | 
			
		||||
		return (uint16_t) exponent & 0x7fff;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define get_nibble(lsu, nibble) \
 | 
			
		||||
	(((nibble) & 1) ? (((lsu)[(nibble)/2] >> 4) & 0x0f) : ((lsu)[(nibble)/2] & 0x0f))
 | 
			
		||||
 | 
			
		||||
#define set_nibble(lsu, nibble, val) do { \
 | 
			
		||||
	if ((nibble) & 1){ \
 | 
			
		||||
		(lsu)[(nibble)/2] = ((lsu)[(nibble)/2] & 0x0f) | (((val) & 0xf) << 4); \
 | 
			
		||||
	} else { \
 | 
			
		||||
		(lsu)[(nibble)/2] = ((lsu)[(nibble)/2] & 0xf0) | ((val) & 0xf); \
 | 
			
		||||
	} \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
static void _zero_remaining_decn(dec80* dest, uint8_t nibble, uint8_t NUM_LSU){
 | 
			
		||||
	while (nibble < NUM_LSU*2){
 | 
			
		||||
		if (nibble & 1){ //odd
 | 
			
		||||
			set_nibble(dest->lsu, nibble, 0);
 | 
			
		||||
			nibble++;
 | 
			
		||||
		} else {
 | 
			
		||||
			dest->lsu[nibble/2] = 0;
 | 
			
		||||
			nibble += 2;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void zero_remaining_64(dec64* dest, uint8_t nibble){
 | 
			
		||||
	_zero_remaining_decn((dec80*) dest, nibble, DEC64_NUM_LSU);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void zero_remaining_80(dec80* dest, uint8_t nibble){
 | 
			
		||||
	_zero_remaining_decn(dest, nibble, DEC80_NUM_LSU);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void remove_leading_zeros(dec80* x){
 | 
			
		||||
	uint8_t nibble;
 | 
			
		||||
 | 
			
		||||
	//find first non-zero nibble
 | 
			
		||||
	for (nibble = 0; nibble < DEC80_NUM_LSU*2; nibble++){
 | 
			
		||||
		if (get_nibble(x->lsu, nibble) != 0){
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (nibble < DEC80_NUM_LSU*2){ //found non-zero nibble
 | 
			
		||||
		uint8_t i;
 | 
			
		||||
		//copy nibbles
 | 
			
		||||
		for (i = 0; nibble < DEC80_NUM_LSU*2; i++, nibble++){
 | 
			
		||||
			set_nibble(x->lsu, i, get_nibble(x->lsu, nibble));
 | 
			
		||||
		}
 | 
			
		||||
		//zero out remaining nibbles, now that number left-aligned
 | 
			
		||||
		zero_remaining_80(x, i);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void build_dec64(dec64* dest, const char* signif_str, int16_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 = 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_dec64_zero(dest);
 | 
			
		||||
		return;
 | 
			
		||||
	} else if (signif_str[0] == '-'){
 | 
			
		||||
		curr_sign = SIGN_NEG_ZERO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//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
 | 
			
		||||
			} else {
 | 
			
		||||
				//multiple '.'s in string
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
				printf("  ERROR: multiple '.'s in string\n");
 | 
			
		||||
#endif
 | 
			
		||||
				set_dec64_NaN(dest);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		} else if (signif_str[i] >= '1' && signif_str[i] <= '9'){
 | 
			
		||||
			if (nibble < DEC64_NUM_LSU*2){
 | 
			
		||||
				set_nibble(dest->lsu, nibble, signif_str[i] - '0');
 | 
			
		||||
			}
 | 
			
		||||
			nibble++;
 | 
			
		||||
			//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/R of decimal point
 | 
			
		||||
			if (SEEN_POINT(curr_sign)){
 | 
			
		||||
				if (num_lr_points <= 0){
 | 
			
		||||
					//seen decimal point, and no left count (or right count already exists)
 | 
			
		||||
					num_lr_points--; //increase right count
 | 
			
		||||
				}
 | 
			
		||||
			} else { //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 < DEC64_NUM_LSU*2){
 | 
			
		||||
					set_nibble(dest->lsu, nibble, 0);
 | 
			
		||||
				}
 | 
			
		||||
				nibble++;
 | 
			
		||||
			}
 | 
			
		||||
			//track number digits L/R of decimal point
 | 
			
		||||
			if (SEEN_POINT(curr_sign)){
 | 
			
		||||
				if (num_lr_points == 0){ //no left count exists
 | 
			
		||||
					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_dec64_zero(dest);
 | 
			
		||||
				return;
 | 
			
		||||
			} else {
 | 
			
		||||
				//not zero, adjust exponent for left-aligned significand input
 | 
			
		||||
				//          or for number of digits past decimal point
 | 
			
		||||
				int8_t new_exponent;
 | 
			
		||||
				if (num_lr_points > 0){ //left count exists
 | 
			
		||||
					assert(DEC64_NUM_LSU*2 > num_lr_points);
 | 
			
		||||
					new_exponent = exponent - ((DEC64_NUM_LSU*2) - num_lr_points);
 | 
			
		||||
				} else if (num_lr_points < 0) { //right count exists
 | 
			
		||||
					// (-num_past_point represents #digits right of decimal)
 | 
			
		||||
					// (this ends up being a subtraction)
 | 
			
		||||
					new_exponent = exponent + num_lr_points;
 | 
			
		||||
				} else {
 | 
			
		||||
					//no change
 | 
			
		||||
					new_exponent = exponent;
 | 
			
		||||
				}
 | 
			
		||||
				//check for underflow
 | 
			
		||||
				if (new_exponent > exponent || exponent < DEC64_MIN_EXP){
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
					printf("   underflow (new_exp, exp)=(%d,%d)\n",
 | 
			
		||||
					         new_exponent, exponent);
 | 
			
		||||
#endif
 | 
			
		||||
					set_dec64_NaN(dest);
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
				//check for overflow
 | 
			
		||||
				if (exponent > DEC64_MAX_EXP){
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
					printf("   overflow (new_exp, exp)=(%d,%d)\n",
 | 
			
		||||
					         new_exponent, exponent);
 | 
			
		||||
#endif
 | 
			
		||||
					set_dec64_NaN(dest);
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
				exponent = new_exponent;
 | 
			
		||||
				if (IS_NEG(curr_sign)){
 | 
			
		||||
					exponent |= 0x8000;
 | 
			
		||||
				} else {
 | 
			
		||||
					exponent &= 0x7fff;
 | 
			
		||||
				}
 | 
			
		||||
				dest->exponent = exponent;
 | 
			
		||||
				zero_remaining_64(dest, i);
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
				printf("   num_lr_points (%d), new_exp (%d), sign (%d), exp (%d)\n",
 | 
			
		||||
				        num_lr_points, new_exponent, curr_sign, exponent);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		i++;
 | 
			
		||||
		assert(i < DECN_BUF_SIZE);
 | 
			
		||||
	} // while 1
 | 
			
		||||
#undef SEEN_POINT
 | 
			
		||||
#undef IS_ZERO
 | 
			
		||||
#undef IS_NEG
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void set_dec64_zero(dec64* dest){
 | 
			
		||||
	uint8_t i;
 | 
			
		||||
 | 
			
		||||
	//clear exponent
 | 
			
		||||
	dest->exponent = 0;
 | 
			
		||||
	//clear nibbles
 | 
			
		||||
	for (i = 0; i < DEC64_NUM_LSU; i++){
 | 
			
		||||
		dest->lsu[i] = 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_dec64_NaN(dec64* dest){
 | 
			
		||||
	uint8_t i;
 | 
			
		||||
 | 
			
		||||
	//clear exponent
 | 
			
		||||
	dest->exponent = 0xff;
 | 
			
		||||
	//clear nibbles
 | 
			
		||||
	for (i = 0; i < DEC64_NUM_LSU; i++){
 | 
			
		||||
		dest->lsu[i] = 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_dec80_NaN(dec80* dest){
 | 
			
		||||
	uint8_t i;
 | 
			
		||||
 | 
			
		||||
	//clear exponent
 | 
			
		||||
	dest->exponent = 0xff;
 | 
			
		||||
	//clear nibbles
 | 
			
		||||
	for (i = 0; i < DEC80_NUM_LSU; i++){
 | 
			
		||||
		dest->lsu[i] = 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void negate_decn(dec80* x){
 | 
			
		||||
	static const int16_t xor_val = 0x8000;
 | 
			
		||||
	(x->exponent) ^= xor_val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int8_t compare_magn(const dec80* a, const dec80* b){ //a<b: -1, a==b: 0, a>b: 1
 | 
			
		||||
	uint8_t a_i, b_i;
 | 
			
		||||
	int16_t a_exp=0, b_exp=0;
 | 
			
		||||
	uint8_t a_trailing_zeros=0, b_trailing_zeros=0;
 | 
			
		||||
	int8_t a_signif_b = 0; //a<b: -1, a==b: 0, a>b: 1
 | 
			
		||||
	//discard leading zeros
 | 
			
		||||
	for (a_i = 0; a_i < DEC80_NUM_LSU*2; a_i++){
 | 
			
		||||
		if (get_nibble(a->lsu, a_i) != 0){
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for (b_i = 0; b_i < DEC80_NUM_LSU*2; b_i++){
 | 
			
		||||
		if (get_nibble(b->lsu, b_i) != 0){
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	//compare signifcands while tracking magnitude
 | 
			
		||||
	for ( ; a_i < DEC80_NUM_LSU*2 && b_i < DEC80_NUM_LSU*2; a_i++, b_i++, a_exp++, b_exp++){
 | 
			
		||||
		//set signif. inequality if this is first digit that is different
 | 
			
		||||
		if (a_signif_b == 0 && (get_nibble(a->lsu, a_i) < get_nibble(b->lsu, b_i))){
 | 
			
		||||
			a_signif_b = -1;
 | 
			
		||||
		} else if (a_signif_b == 0 && (get_nibble(a->lsu, a_i) > get_nibble(b->lsu, b_i))){
 | 
			
		||||
			a_signif_b = 1;
 | 
			
		||||
		}
 | 
			
		||||
		//track trailing zeros (a)
 | 
			
		||||
		if (get_nibble(a->lsu, a_i) == 0){
 | 
			
		||||
			a_trailing_zeros++;
 | 
			
		||||
		} else {
 | 
			
		||||
			a_trailing_zeros = 0;
 | 
			
		||||
		}
 | 
			
		||||
		//                     (b)
 | 
			
		||||
		if (get_nibble(b->lsu, b_i) == 0){
 | 
			
		||||
			b_trailing_zeros++;
 | 
			
		||||
		} else {
 | 
			
		||||
			b_trailing_zeros = 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	//done with at least one, make sure both are done (a)
 | 
			
		||||
	for ( ; a_i < DEC80_NUM_LSU*2; a_i++, a_exp++){
 | 
			
		||||
		//track trailing zeros
 | 
			
		||||
		if (get_nibble(a->lsu, a_i) == 0){
 | 
			
		||||
			a_trailing_zeros++;
 | 
			
		||||
		} else {
 | 
			
		||||
			a_trailing_zeros = 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	//                                                (b)
 | 
			
		||||
	for ( ; b_i < DEC80_NUM_LSU*2; b_i++, b_exp++){
 | 
			
		||||
		//track trailing zeros
 | 
			
		||||
		if (get_nibble(b->lsu, b_i) == 0){
 | 
			
		||||
			b_trailing_zeros++;
 | 
			
		||||
		} else {
 | 
			
		||||
			b_trailing_zeros = 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	//calculate exponents
 | 
			
		||||
	a_exp += get_exponent(a);
 | 
			
		||||
	b_exp += get_exponent(b);
 | 
			
		||||
	a_exp -= a_trailing_zeros;
 | 
			
		||||
	b_exp -= b_trailing_zeros;
 | 
			
		||||
	//compare exponents
 | 
			
		||||
	if (a_exp > b_exp){
 | 
			
		||||
		return 1;
 | 
			
		||||
	} else if (a_exp < b_exp){
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	//exponents equal, compare by significand
 | 
			
		||||
	return a_signif_b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int8_t compare_decn(const dec80* a, const dec80* b){ //a<b: -1, a==b: 0, a>b: 1
 | 
			
		||||
	int8_t is_neg;
 | 
			
		||||
 | 
			
		||||
	//handle zero special cases
 | 
			
		||||
	if (decn_is_zero(a) && decn_is_zero(b)){
 | 
			
		||||
		return 0;
 | 
			
		||||
	} else if (decn_is_zero(a)){
 | 
			
		||||
		if (b->exponent < 0){
 | 
			
		||||
			return 1;
 | 
			
		||||
		} else {
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (decn_is_zero(b)){
 | 
			
		||||
		if (a->exponent < 0){
 | 
			
		||||
			return -1;
 | 
			
		||||
		} else {
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	//handle cases where signs differ
 | 
			
		||||
	if (a->exponent < 0 && b->exponent > 0){
 | 
			
		||||
		return -1;
 | 
			
		||||
	} else if (a->exponent > 0 && b->exponent < 0){
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
	//signs must now be the same, either both pos, or both neg
 | 
			
		||||
	is_neg = (a->exponent < 0 ? -1 : 1);
 | 
			
		||||
 | 
			
		||||
	return is_neg * compare_magn(a, b);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sub_mag(dec80* acc, const dec80* x){
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//WARNING: for add_decn() function only
 | 
			
		||||
//rescales acc up to exponent (increase exponent of acc in abs value, while doing shifts)
 | 
			
		||||
//the actual value of acc->exponent remains unchanged
 | 
			
		||||
//both acc, and the number from which exponent was taking must be stripped of leading 0s first
 | 
			
		||||
static void _incr_exp(dec80* acc, int16_t exponent){
 | 
			
		||||
	int16_t curr_exp = get_exponent(acc);
 | 
			
		||||
	uint8_t is_neg = (acc->exponent < 0 ? 1 : 0);
 | 
			
		||||
#ifdef DEBUG_ADD
 | 
			
		||||
	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
 | 
			
		||||
		int8_t i;
 | 
			
		||||
		for (i = DEC80_NUM_LSU*2 - 1 - 1; i >= 0; i--){
 | 
			
		||||
			set_nibble(acc->lsu, i + 1, get_nibble(acc->lsu, i));
 | 
			
		||||
		}
 | 
			
		||||
		acc->lsu[0] &= 0xf0; //0 gets shifted into most significant digit
 | 
			
		||||
		curr_exp++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//curr_exp does NOT get written back to acc->exponent
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void add_decn(dec80* acc, const dec80* x){
 | 
			
		||||
	dec80 tmp;
 | 
			
		||||
	int8_t rel;
 | 
			
		||||
	uint8_t carry = 0;
 | 
			
		||||
	int8_t i;
 | 
			
		||||
 | 
			
		||||
	//check if zero
 | 
			
		||||
	if (decn_is_zero(x)){
 | 
			
		||||
		return;
 | 
			
		||||
	} else if (decn_is_zero(acc)){
 | 
			
		||||
		copy_decn(acc, x);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	//handle cases where signs differ
 | 
			
		||||
	if (acc->exponent < 0 && x->exponent > 0){
 | 
			
		||||
		// -acc, +x
 | 
			
		||||
		rel = compare_magn(acc, x);
 | 
			
		||||
		if (rel == 1){
 | 
			
		||||
			sub_mag(acc, x);
 | 
			
		||||
			negate_decn(acc);
 | 
			
		||||
			return;
 | 
			
		||||
		} else if (rel == -1){
 | 
			
		||||
			copy_decn(&tmp, x);
 | 
			
		||||
			sub_mag(&tmp, acc);
 | 
			
		||||
			copy_decn(acc, &tmp);
 | 
			
		||||
			return;
 | 
			
		||||
		} else { //equal
 | 
			
		||||
			set_dec80_zero(acc);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (acc->exponent > 0 && x->exponent < 0){
 | 
			
		||||
		// +acc, -x
 | 
			
		||||
		rel = compare_magn(acc, x);
 | 
			
		||||
		if (rel == 1){
 | 
			
		||||
			sub_mag(acc, x);
 | 
			
		||||
			return;
 | 
			
		||||
		} else if (rel == -1){
 | 
			
		||||
			copy_decn(&tmp, x);
 | 
			
		||||
			sub_mag(&tmp, acc);
 | 
			
		||||
			negate_decn(&tmp);
 | 
			
		||||
			copy_decn(acc, &tmp);
 | 
			
		||||
			return;
 | 
			
		||||
		} else { //equal
 | 
			
		||||
			set_dec80_zero(acc);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	//signs must now be the same, begin adding
 | 
			
		||||
	copy_decn(&tmp, x);
 | 
			
		||||
	//normalize
 | 
			
		||||
	remove_leading_zeros(acc);
 | 
			
		||||
	remove_leading_zeros(&tmp);
 | 
			
		||||
#ifdef DEBUG_ADD
 | 
			
		||||
	extern char buf[DECN_BUF_SIZE];
 | 
			
		||||
	dec80_to_str(buf, acc);
 | 
			
		||||
	printf("rem_leading_zeros acc: %s\n", buf);
 | 
			
		||||
	dec80_to_str(buf, &tmp);
 | 
			
		||||
	printf("rem_leading_zeros tmp: %s\n", buf);
 | 
			
		||||
#endif
 | 
			
		||||
	rel = compare_magn(acc, &tmp);
 | 
			
		||||
	if (rel == 1){
 | 
			
		||||
		_incr_exp(&tmp, get_exponent(acc));
 | 
			
		||||
	} else if (rel == -1){
 | 
			
		||||
		_incr_exp(acc, get_exponent(&tmp));
 | 
			
		||||
	}
 | 
			
		||||
#ifdef DEBUG_ADD
 | 
			
		||||
	extern char buf[DECN_BUF_SIZE];
 | 
			
		||||
	dec80_to_str(buf, acc);
 | 
			
		||||
	printf("incr_exp acc: %s\n", buf);
 | 
			
		||||
	dec80_to_str(buf, &tmp);
 | 
			
		||||
	printf("incr_exp tmp: %s\n", buf);
 | 
			
		||||
#endif
 | 
			
		||||
	//do addition
 | 
			
		||||
	for (i = DEC80_NUM_LSU*2 - 1; i >= 0; i--){
 | 
			
		||||
		uint8_t digit = get_nibble(acc->lsu, i) + get_nibble(tmp.lsu, i) + carry;
 | 
			
		||||
		set_nibble(acc->lsu, i, digit % 10);
 | 
			
		||||
		carry = digit / 10;
 | 
			
		||||
		assert(carry < 10);
 | 
			
		||||
	}
 | 
			
		||||
	//may need to rescale number
 | 
			
		||||
	if (carry > 0){
 | 
			
		||||
		int16_t curr_exp = get_exponent(acc);
 | 
			
		||||
		rel = (acc->exponent < 0 ? 1 : 0); //is_neg?
 | 
			
		||||
#ifdef DEBUG_ADD
 | 
			
		||||
		printf("carry out: %d", carry);
 | 
			
		||||
#endif
 | 
			
		||||
		//shift right
 | 
			
		||||
		for (i = DEC80_NUM_LSU*2 - 1 - 1; i >= 0; i--){
 | 
			
		||||
			set_nibble(acc->lsu, i + 1, get_nibble(acc->lsu, i));
 | 
			
		||||
		}
 | 
			
		||||
		acc->lsu[0] &= 0xf0; //carry gets shifted into most significant digit
 | 
			
		||||
		acc->lsu[0] |= carry;
 | 
			
		||||
		curr_exp++;
 | 
			
		||||
		if (rel){ //is_neg
 | 
			
		||||
			acc->exponent = curr_exp | 0x8000;
 | 
			
		||||
		} else {
 | 
			
		||||
			acc->exponent = curr_exp | 0x7fff;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//buf should hold at least 18 + 4 + 5 + 1 = 28
 | 
			
		||||
static void decn_to_str(char* buf, const dec80* x, uint8_t NUM_LSU){
 | 
			
		||||
	uint8_t i = 0;
 | 
			
		||||
	uint8_t nibble;
 | 
			
		||||
	int16_t exponent;
 | 
			
		||||
	uint8_t trailing_zeros = 0;
 | 
			
		||||
 | 
			
		||||
	//check sign of number
 | 
			
		||||
	if (x->exponent < 0){
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
		printf ("  negative  ");
 | 
			
		||||
#endif
 | 
			
		||||
		buf[i] = '-';
 | 
			
		||||
		i++;
 | 
			
		||||
	}
 | 
			
		||||
	//discard leading zeros
 | 
			
		||||
	for (nibble = 0; nibble < NUM_LSU*2; nibble++){
 | 
			
		||||
		if (get_nibble(x->lsu, nibble) != 0){
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
	printf ("  leading 0s discarded (%d)  ", nibble);
 | 
			
		||||
#endif
 | 
			
		||||
	//print 1st nonzero
 | 
			
		||||
 | 
			
		||||
	//handle corner case
 | 
			
		||||
	if (nibble == NUM_LSU*2){
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
		printf ("  corner case, set to 0  ");
 | 
			
		||||
#endif
 | 
			
		||||
		buf[0] = '0';
 | 
			
		||||
		buf[1] = '\0';
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf[i] = get_nibble(x->lsu, nibble) + '0';
 | 
			
		||||
	i++;
 | 
			
		||||
	nibble++;
 | 
			
		||||
	buf[i] = '.';
 | 
			
		||||
	i++;
 | 
			
		||||
	exponent = 0;
 | 
			
		||||
	//print rest of significand
 | 
			
		||||
	for ( ; nibble < NUM_LSU*2; nibble++, i++, exponent++){
 | 
			
		||||
		buf[i] = get_nibble(x->lsu, nibble) + '0';
 | 
			
		||||
		if (get_nibble(x->lsu, nibble) == 0){
 | 
			
		||||
			trailing_zeros++;
 | 
			
		||||
		} else {
 | 
			
		||||
			trailing_zeros = 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	//calculate exponent
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
	printf ("  exponent (%d,", exponent);
 | 
			
		||||
#endif
 | 
			
		||||
	exponent += get_exponent(x);
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
	printf ("%d,", exponent);
 | 
			
		||||
#endif
 | 
			
		||||
	exponent -= trailing_zeros;
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
	printf ("%d)  ", exponent);
 | 
			
		||||
#endif
 | 
			
		||||
	//remove trailing zeros
 | 
			
		||||
	i -= trailing_zeros;
 | 
			
		||||
 | 
			
		||||
	if (exponent != 0){
 | 
			
		||||
		buf[i] = 'E';
 | 
			
		||||
		i++;
 | 
			
		||||
		if (exponent < 0){
 | 
			
		||||
			buf[i] = '-';
 | 
			
		||||
			i++;
 | 
			
		||||
			u32str(-exponent, &buf[i], 10); //adds null terminator
 | 
			
		||||
		} else {
 | 
			
		||||
			u32str(exponent, &buf[i], 10); //adds null terminator
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		//null terminate
 | 
			
		||||
		buf[i] = '\0';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
	printf ("  final i (%d)  ", i);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dec80_to_str(char* buf, const dec80* x){
 | 
			
		||||
	decn_to_str(buf, x, DEC80_NUM_LSU);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dec64_to_str(char* buf, const dec64* x){
 | 
			
		||||
	decn_to_str(buf, (const dec80*) x, DEC64_NUM_LSU);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										61
									
								
								src/decn/decn.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/decn/decn.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
			
		||||
/*
 | 
			
		||||
 * decn.h
 | 
			
		||||
 *
 | 
			
		||||
 *  Created on: Mar 21, 2019
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef SRC_DEC_DECN_H_
 | 
			
		||||
#define SRC_DEC_DECN_H_
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#define DEC64_NUM_LSU 8
 | 
			
		||||
#define DEC80_NUM_LSU 10
 | 
			
		||||
 | 
			
		||||
//follow IEEE754 standard
 | 
			
		||||
#define DEC64_MIN_EXP (-383)
 | 
			
		||||
#define DEC64_MAX_EXP (384)
 | 
			
		||||
 | 
			
		||||
//allow full range, but reserve -16384 for special numbers
 | 
			
		||||
#define DEC80_MIN_EXP (-16383)
 | 
			
		||||
#define DEC80_MAX_EXP   16383
 | 
			
		||||
 | 
			
		||||
//decimal64 unpacked into 72 bits
 | 
			
		||||
typedef struct {
 | 
			
		||||
	int16_t exponent;
 | 
			
		||||
	uint8_t lsu[DEC64_NUM_LSU]; //lus[0] holds most-significant 2 nibbles
 | 
			
		||||
} dec64;
 | 
			
		||||
 | 
			
		||||
//decimal80 unpacked into 80 bits
 | 
			
		||||
// for computation
 | 
			
		||||
typedef struct {
 | 
			
		||||
	int16_t exponent; //MSBit is sign of dec80 number, bottom 15 bits are 2's Compl exponent
 | 
			
		||||
	uint8_t lsu[DEC80_NUM_LSU]; //lus[0] holds most-significant 2 nibbles
 | 
			
		||||
} dec80;
 | 
			
		||||
 | 
			
		||||
//remove sign bit, and return 15 bit exponent sign-extended to 16 bits
 | 
			
		||||
int16_t get_exponent(const dec80* x);
 | 
			
		||||
 | 
			
		||||
void dec64to80(dec80* dest, const dec64* src);
 | 
			
		||||
void dec80to64(dec64* dest, const dec80* src);
 | 
			
		||||
 | 
			
		||||
void copy_decn(dec80* dest, const dec80* src);
 | 
			
		||||
 | 
			
		||||
void build_dec64(dec64* dest, const char* signif_str, int16_t exponent);
 | 
			
		||||
//void build_dec80(dec80* dest, const char* signif_str, int16_t exponent);
 | 
			
		||||
 | 
			
		||||
void set_dec64_zero(dec64* dest);
 | 
			
		||||
void set_dec80_zero(dec80* dest);
 | 
			
		||||
void set_dec64_NaN(dec64* dest);
 | 
			
		||||
void set_dec80_NaN(dec80* dest);
 | 
			
		||||
 | 
			
		||||
void negate_decn(dec80* x);
 | 
			
		||||
int8_t compare_decn(dec80* a, dec80* b); //a<b: -1, a==b: 0, a>b: 1
 | 
			
		||||
void add_decn(dec80* acc, const dec80* x);
 | 
			
		||||
 | 
			
		||||
//buf should hold at least 18 + 4 + 5 + 1 = 28
 | 
			
		||||
#define DECN_BUF_SIZE 28
 | 
			
		||||
void dec80_to_str(char* buf, const dec80* x);
 | 
			
		||||
void dec64_to_str(char* buf, const dec64* x);
 | 
			
		||||
 | 
			
		||||
#endif /* SRC_DEC_DECN_H_ */
 | 
			
		||||
							
								
								
									
										40
									
								
								src/decn/decn_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/decn/decn_test.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
			
		||||
/*
 | 
			
		||||
 * decn_test.c
 | 
			
		||||
 *
 | 
			
		||||
 *  Created on: Mar 21, 2019
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include "decn.h"
 | 
			
		||||
 | 
			
		||||
char buf[DECN_BUF_SIZE];
 | 
			
		||||
 | 
			
		||||
int main(void){
 | 
			
		||||
	dec64 a;
 | 
			
		||||
	dec80 acc, b;
 | 
			
		||||
 | 
			
		||||
	build_dec64(&a,  "9.234567890123456", 3);
 | 
			
		||||
	dec64_to_str(buf, &a);
 | 
			
		||||
	printf("   a: %s\n", buf);
 | 
			
		||||
 | 
			
		||||
	dec64to80(&acc, &a);
 | 
			
		||||
	dec80_to_str(buf, &acc);
 | 
			
		||||
	printf(" acc: %s\n", buf);
 | 
			
		||||
	negate_decn(&acc);
 | 
			
		||||
	dec80_to_str(buf, &acc);
 | 
			
		||||
	printf("-acc:  %s\n", buf);
 | 
			
		||||
	build_dec64(&a, "-92.3456789012345678", 1);
 | 
			
		||||
	dec64to80(&b, &a);
 | 
			
		||||
	dec80_to_str(buf, &b);
 | 
			
		||||
	printf("   b: %s\n", buf);
 | 
			
		||||
 | 
			
		||||
	dec80_to_str(buf, &acc);
 | 
			
		||||
	printf("-acc:  %s\n", buf);
 | 
			
		||||
 | 
			
		||||
	add_decn(&acc, &b);
 | 
			
		||||
	dec80_to_str(buf, &acc);
 | 
			
		||||
	printf("b - a: %s\n", buf);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								src/utils.c
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/utils.c
									
									
									
									
									
								
							@ -1,5 +1,15 @@
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include "utils.h"
 | 
			
		||||
 | 
			
		||||
#ifdef DESKTOP
 | 
			
		||||
void _delay_ms(uint8_t ms){
 | 
			
		||||
	//TODO:
 | 
			
		||||
}
 | 
			
		||||
void _delay_us(uint8_t us)
 | 
			
		||||
{
 | 
			
		||||
	//TODO:
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
void _delay_ms(uint8_t ms)
 | 
			
		||||
{
 | 
			
		||||
	// delay function, tuned for 11.583 MHz clock
 | 
			
		||||
@ -35,6 +45,7 @@ void _delay_us(uint8_t us)
 | 
			
		||||
		djnz dpl, us_delay$
 | 
			
		||||
	__endasm;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
char* u32str(uint32_t x, char* buf, uint8_t base)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user