change to base-100 with implicit decimal point

- addition working for test case
This commit is contained in:
Jeff Wang 2019-03-26 02:38:56 -04:00
parent d351995294
commit ad9a9f55ad
3 changed files with 191 additions and 240 deletions

View File

@ -7,7 +7,7 @@
#include "../utils.h"
//#define DEBUG
#define DEBUG
#define DEBUG_ADD
#ifndef DESKTOP
@ -28,25 +28,6 @@
#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;
@ -67,60 +48,55 @@ int16_t get_exponent(const dec80* x){
}
}
#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_dec80(dec80* dest, uint8_t digit100){
for ( ; digit100 < DEC80_NUM_LSU; digit100++){
dest->lsu[digit100] = 0;
}
}
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;
uint8_t digit100;
uint8_t is_negative = (x->exponent < 0 ? 1 : 0);
uint8_t exponent = get_exponent(x);
//find first non-zero nibble
for (nibble = 0; nibble < DEC80_NUM_LSU*2; nibble++){
if (get_nibble(x->lsu, nibble) != 0){
//find first non-zero digit100
for (digit100 = 0; digit100 < DEC80_NUM_LSU; digit100++){
if (x->lsu[digit100] != 0){
break;
}
}
exponent -= digit100 * 2; //base 100
if (nibble < DEC80_NUM_LSU*2){ //found non-zero nibble
if (digit100 != 0 && digit100 < DEC80_NUM_LSU){ //found non-zero digit100
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));
//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);
//write back exponent
if (is_negative){
x->exponent = exponent | 0x8000;
} else {
x->exponent = exponent & 0x7fff;
}
//zero out remaining nibbles, now that number left-aligned
zero_remaining_80(x, i);
}
}
static void shift_right(dec80* x){
uint8_t high = 0, low = 0, old_low = 0;
uint8_t i;
for (i = 0; i < DEC80_NUM_LSU; i++){
high = x->lsu[i] / 10;
low = x->lsu[i] % 10;
x->lsu[i] = high + (old_low*10);
old_low = low;
}
}
void build_dec64(dec64* dest, const char* signif_str, int16_t exponent){
void build_dec80(dec80* dest, const char* signif_str, int16_t exponent){
enum {
SIGN_ZERO,
SIGN_ZERO_SEEN_POINT,
@ -139,13 +115,14 @@ void build_dec64(dec64* dest, const char* signif_str, int16_t exponent){
#define IS_NEG(curr_sign) ((curr_sign) >= SIGN_NEG)
uint8_t i = 0;
uint8_t nibble = 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_dec64_zero(dest);
set_dec80_zero(dest);
return;
} else if (signif_str[0] == '-'){
curr_sign = SIGN_NEG_ZERO;
@ -162,14 +139,18 @@ void build_dec64(dec64* dest, const char* signif_str, int16_t exponent){
#ifdef DEBUG
printf(" ERROR: multiple '.'s in string\n");
#endif
set_dec64_NaN(dest);
set_dec80_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');
if (nibble_i < DEC80_NUM_LSU*2){
if (nibble_i & 1) { //odd
dest->lsu[nibble_i/2] = save_nibble * 10 + (signif_str[i] - '0');
} else {
save_nibble = signif_str[i] - '0';
}
}
nibble++;
nibble_i++;
//track sign
if (curr_sign == SIGN_ZERO){
curr_sign = SIGN_POS;
@ -181,26 +162,27 @@ void build_dec64(dec64* dest, const char* signif_str, int16_t exponent){
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
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 < DEC64_NUM_LSU*2){
set_nibble(dest->lsu, nibble, 0);
if (nibble_i < DEC80_NUM_LSU*2){
if (nibble_i & 1) { //odd
dest->lsu[nibble_i/2] = save_nibble * 10 + 0;
} else {
save_nibble = 0;
}
}
nibble++;
nibble_i++;
}
//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
if (IS_ZERO(curr_sign)){ //tracking 0s to right of point
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
@ -210,52 +192,67 @@ void build_dec64(dec64* dest, const char* signif_str, int16_t exponent){
} else if (signif_str[i] == '\0'){ //done
if (IS_ZERO(curr_sign)){
//zero
set_dec64_zero(dest);
set_dec80_zero(dest);
return;
} else {
//not zero, adjust exponent for left-aligned significand input
// or for number of digits past decimal point
//not zero
int8_t new_exponent;
//write out saved nibble, if it exists
// (saved while nibble_i even, nibble_i then incremented to odd)
if (nibble_i & 1){ //odd
dest->lsu[nibble_i/2] = save_nibble * 10;
nibble_i++; //increment for zeroing out
}
//zero out any old data
zero_remaining_dec80(dest, 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
assert(DEC64_NUM_LSU*2 > num_lr_points);
new_exponent = exponent - ((DEC64_NUM_LSU*2) - num_lr_points);
assert(DEC80_NUM_LSU*2 > num_lr_points);
new_exponent = exponent + (num_lr_points - 1); //1 digit left of implicit point
//check for overflow
if (new_exponent < exponent || exponent > DEC80_MAX_EXP){
#ifdef DEBUG
printf(" overflow (new_exp, exp)=(%d,%d)\n",
new_exponent, exponent);
#endif
set_dec80_NaN(dest);
return;
}
} else if (num_lr_points < 0) { //right count exists
// (-num_past_point represents #digits right of decimal)
// (-num_past_point represents #0s right of decimal)
// (this ends up being a subtraction)
new_exponent = exponent + num_lr_points;
new_exponent -= 1; //decimal point after 1st non-zero number
//check for underflow
if (new_exponent > exponent || exponent < DEC80_MIN_EXP){
#ifdef DEBUG
printf(" underflow (new_exp, exp)=(%d,%d)\n",
new_exponent, exponent);
#endif
set_dec80_NaN(dest);
return;
}
} 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;
//set negative bit
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);
printf(" ");
for (i = 0; i < DEC80_NUM_LSU; i++){
printf("%02d,", dest->lsu[i]);
}
printf("\n");
#endif
return;
@ -270,16 +267,6 @@ void build_dec64(dec64* dest, const char* signif_str, int16_t exponent){
}
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;
@ -303,21 +290,10 @@ static uint8_t decn_is_zero(const dec80* x){
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
//set exponent to special val
dest->exponent = 0xff;
//clear nibbles
for (i = 0; i < DEC80_NUM_LSU; i++){
@ -333,63 +309,36 @@ void negate_decn(dec80* x){
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){
for (a_i = 0; a_i < DEC80_NUM_LSU; a_i++){
if (a->lsu[a_i] != 0){
if (a->lsu[a_i] > 10){
a_exp++;
}
break;
}
}
for (b_i = 0; b_i < DEC80_NUM_LSU*2; b_i++){
if (get_nibble(b->lsu, b_i) != 0){
for (b_i = 0; b_i < DEC80_NUM_LSU; b_i++){
if (b->lsu[b_i] != 0){
if (b->lsu[b_i] > 10){
b_exp++;
}
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++){
for ( ; a_i < DEC80_NUM_LSU && b_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 && (get_nibble(a->lsu, a_i) < get_nibble(b->lsu, b_i))){
if (a_signif_b == 0 && (a->lsu[a_i] < 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))){
} else if (a_signif_b == 0 && (a->lsu[a_i] > 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;
@ -449,11 +398,7 @@ static void _incr_exp(dec80* acc, int16_t exponent){
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
shift_right(acc);
curr_exp++;
}
@ -533,11 +478,11 @@ void add_decn(dec80* acc, const dec80* x){
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);
for (i = DEC80_NUM_LSU - 1; i >= 0; i--){
uint8_t digit100 = acc->lsu[i] + tmp.lsu[i] + carry;
acc->lsu[i] = digit100 % 100;
carry = digit100 / 100;
assert(carry < 100);
}
//may need to rescale number
if (carry > 0){
@ -547,12 +492,17 @@ void add_decn(dec80* acc, const dec80* x){
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));
if (carry < 10){
shift_right(acc);
acc->lsu[0] += carry*10; //carry gets shifted into most significant digit
curr_exp++;
} else {
shift_right(acc);
shift_right(acc);
acc->lsu[0] = carry;
curr_exp+=2;
}
acc->lsu[0] &= 0xf0; //carry gets shifted into most significant digit
acc->lsu[0] |= carry;
curr_exp++;
//track sign
if (rel){ //is_neg
acc->exponent = curr_exp | 0x8000;
} else {
@ -562,10 +512,10 @@ void add_decn(dec80* acc, const dec80* x){
}
//buf should hold at least 18 + 4 + 5 + 1 = 28
static void decn_to_str(char* buf, const dec80* x, uint8_t NUM_LSU){
void dec80_to_str(char* buf, const dec80* x){
uint8_t i = 0;
uint8_t nibble;
int16_t exponent;
uint8_t digit100;
int16_t exponent = 0;
uint8_t trailing_zeros = 0;
//check sign of number
@ -577,18 +527,19 @@ static void decn_to_str(char* buf, const dec80* x, uint8_t NUM_LSU){
i++;
}
//discard leading zeros
for (nibble = 0; nibble < NUM_LSU*2; nibble++){
if (get_nibble(x->lsu, nibble) != 0){
for (digit100 = 0; digit100 < DEC80_NUM_LSU; digit100++){
if (x->lsu[digit100] != 0){
break;
}
exponent -= 2; //base 100
}
#ifdef DEBUG
printf (" leading 0s discarded (%d) ", nibble);
printf (" leading 0s discarded (%d) ", digit100);
#endif
//print 1st nonzero
//handle corner case
if (nibble == NUM_LSU*2){
//handle corner case of 0
if (digit100 == DEC80_NUM_LSU){
#ifdef DEBUG
printf (" corner case, set to 0 ");
#endif
@ -597,32 +548,54 @@ static void decn_to_str(char* buf, const dec80* x, uint8_t NUM_LSU){
return;
}
buf[i] = get_nibble(x->lsu, nibble) + '0';
i++;
nibble++;
buf[i] = '.';
i++;
exponent = 0;
//print 1st two digits
if (x->lsu[digit100] >= 10){ //2 digits
buf[i] = (x->lsu[digit100] / 10) + '0';
i++;
buf[i] = '.';
i++;
buf[i] = (x->lsu[digit100] % 10) + '0';
i++;
} else { //1 digit
buf[i] = x->lsu[digit100] + '0';
i++;
buf[i] = '.';
i++;
}
digit100++;
//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;
for ( ; digit100 < DEC80_NUM_LSU; digit100++){
if (x->lsu[digit100] > 10){ //2 digits
buf[i] = (x->lsu[digit100] / 10) + '0';
i++;
buf[i] = (x->lsu[digit100] % 10) + '0';
i++;
//track trailing 0s
if ((x->lsu[digit100] % 10) == 0){
trailing_zeros++;
} else {
trailing_zeros = 0;
}
} else { //1 digit
buf[i] = '0';
i++;
buf[i] = x->lsu[digit100] + '0';
i++;
//track trailing 0s
if ((x->lsu[digit100]) == 0){
trailing_zeros += 2;
} else {
trailing_zeros = 0;
}
}
}
//calculate exponent
#ifdef DEBUG
printf (" exponent (%d,", exponent);
#endif
exponent += get_exponent(x);
exponent += get_exponent(x); //base 100
#ifdef DEBUG
printf ("%d,", exponent);
#endif
exponent -= trailing_zeros;
#ifdef DEBUG
printf ("%d) ", exponent);
printf ("%d)", exponent);
#endif
//remove trailing zeros
i -= trailing_zeros;
@ -633,9 +606,9 @@ static void decn_to_str(char* buf, const dec80* x, uint8_t NUM_LSU){
if (exponent < 0){
buf[i] = '-';
i++;
u32str(-exponent, &buf[i], 10); //adds null terminator
u32str(-exponent, &buf[i], 10); //adds null terminator automatically
} else {
u32str(exponent, &buf[i], 10); //adds null terminator
u32str(exponent, &buf[i], 10); //adds null terminator automatically
}
} else {
//null terminate
@ -647,12 +620,5 @@ static void decn_to_str(char* buf, const dec80* x, uint8_t NUM_LSU){
#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);
}

View File

@ -9,44 +9,30 @@
#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)
#define DEC80_NUM_LSU 9
//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
uint8_t lsu[DEC80_NUM_LSU]; //lsu[0] holds most-significant 2 digits (base 100)
//implicit decimal point between (lsu[0]/100) and (lsu[0]%100)
} 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 dec64to80(dec80* dest, const dec64* 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 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);
@ -56,6 +42,5 @@ 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_ */

View File

@ -10,21 +10,21 @@
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);
build_dec80(&acc, "0.0009234567890123456", 7);
dec80_to_str(buf, &acc);
printf(" acc: %s\n", buf);
build_dec80(&acc, "9.234567890123456", 3);
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);
build_dec80(&b, "-92.3456789012345678", 1);
dec80_to_str(buf, &b);
printf(" b: %s\n", buf);