/****************************************************************************
48 bit integer math
Picuino Development Board
Creative Commons Reconocimiento-CompartirIgual 3.0 España License.
https://sites.google.com/site/picuino
****************************************************************************/
#include "math48.h"
/****************************************************************************
GLOBAL VARS AND DEFINITIONS
****************************************************************************/
// Variable for store answer and status register
static uint48 ans;
static struct {
unsigned char carry:1;
unsigned char overflow:1;
} st48;
/****************************************************************************
BASIC MATH ROUTINES
****************************************************************************/
/*
Compare 2 uint48 numbers
(a = b) return 0
(a > b) return 1
(a < b) return -1
*/
signed char comp48(uint48 a, uint48 b) {
if (a.dword > b.dword) return 1;
if (a.dword < b.dword) return -1;
if (a.word[0] > b.word[0]) return 1;
if (a.word[0] < b.word[0]) return -1;
return 0;
}
/*
Sub 2 uint48 numbers
return a - b
*/
uint48 sub48(uint48 a, uint48 b) {
ans.dword = a.dword - b.dword;
if (b.word[0] > a.word[0])
ans.dword--;
ans.word[0] = a.word[0] - b.word[0];
return ans;
}
/*
Add 2 uint48 numbers
return a + b
*/
uint48 add48(uint48 a, uint48 b) {
ans.dword = a.dword + b.dword;
ans.word[0] = a.word[0] + b.word[0];
if (ans.word[0] < a.word[0])
ans.dword++;
return ans;
}
/*
Add 2 uint48 numbers
return a + b
*/
uint48 inc48(uint48 a) {
ans.word[0] = a.word[0] + 1;
ans.dword = a.dword;
if (ans.word[0] == 0)
ans.dword++;
return ans;
}
/*
Rotate left
return a<<1
*/
uint48 _rl48(uint48 a) {
st48.carry = 0;
if (a.b47==1) st48.carry = 1;
ans.dword = a.dword<<1;
if (a.b15==1) ans.b16 = 1;
ans.word[0] = a.word[0]<<1;
if (st48.carry == 1)
ans.b0 = 1;
return ans;
}
/*
Rotate right
return a>>1
*/
uint48 _rr48(uint48 a) {
st48.carry = 0;
if (a.b0==1) st48.carry = 1;
ans.word[0] = a.word[0]>>1;
if (a.b16==1) ans.b15 = 1;
ans.dword = a.dword>>1;
if (st48.carry == 1)
ans.b47 = 1;
return ans;
}
/*
Shift left
return a<<1
*/
uint48 sl48(uint48 a) {
st48.carry = 0;
if (a.b47==1) st48.carry = 1;
ans.dword = a.dword<<1;
if (a.b15==1) ans.b16 = 1;
ans.word[0] = a.word[0]<<1;
return ans;
}
/*
Shift right
return a>>1
*/
uint48 sr48(uint48 a) {
st48.carry = 0;
if (a.b0==1) st48.carry = 1;
ans.word[0] = a.word[0]>>1;
if (a.b16==1) ans.b15 = 1;
ans.dword = a.dword>>1;
return ans;
}
/****************************************************************************
DECIMAL MATH ROUTINES
****************************************************************************/
/*
Multiply by 10
return a*10
*/
uint48 mul10_48(uint48 a) {
a = sl48(a);
ans = sl48(a);
ans = sl48(ans);
ans = add48(a, ans);
return ans;
}
/*
Rational division
rat = a/b * 10^dot
rat = [signed char dot] + [uint 40bit number]
*/
uint48 div48_rat(uint48 a, uint48 b) {
uint48 rat;
// Clear rational result
LOAD48(rat, 0);
// Adjust integers
for(i=46; i>0; i--) {
if (a.b46 == 1) break;
a = sl48(a);
exp--;
}
for(i=46; i>0; i--) {
if (b.b46 == 1) break;
b = sl48(b);
}
// if (argument == zero) return zero
if (b.b46 == 0 || a.b46 == 0)
return rat;
// divide a / b
do {
if (comp48(a, b) >= 0) {
a = sub48(a, b);
rat.b0 = 1;
}
a = sl48(a);
rat = sl48(rat);
exp--;
} while(rat.b39==0); // max resolution = 40bit = 12 decimal digits
// Adjust binary to decimal ratio
dot = 0;
for(i=30; i>0; i--) {
// rat = rat * 10/8
if (exp>-3) break;
if (rat.byte[4]>=186) break; // avoid overflow
inc48(rat);
sr48(ans);
sr48(ans);
rat = add48(rat, ans);
dot--;
// rat = rat / 2
if (rat.b40==1) {
inc48(rat);
rat = sr48(ans); // 1/2
}
};
inc48(rat);
rat = sr48(ans);
}
// Add exponent and return
rat.byte[5] = dot;
return rat;
}
/*
Transform 48bit binary to BCD
sdlong = byte[8]
Delay = 12500 cycles
*/
#define DEC_DIG_48 15
void ltoa48(unsigned char *str, static uint48 bin) {
unsigned char maxdig, digcnt, bitcnt;
static unsigned char *p, carry;
// Clear string
p = str;
digcnt = DEC_DIG_48 + 1;
do *p++ = 0; while (--digcnt);
// Transform binary to BCD
bitcnt = 48;
maxdig = (DEC_DIG_48 - 1);
str += (DEC_DIG_48 - 1);
do {
// shift binary number left with carry
carry = 0;
if (bin.b47==1) carry |= 1;
bin = sl48(bin);
// shift decimal number left with carry
p = str;
digcnt = DEC_DIG_48 - maxdig;
do {
carry = (*p<<1) + carry;
if (carry>=10) {
*p-- = carry - 10;
carry = 1;
if (digcnt==1) {
maxdig--;
digcnt++;
}
}
else {
*p-- = carry;
carry = 0;
}
} while(--digcnt);
} while(--bitcnt);
// Transform BCD to ASCII
digcnt = DEC_DIG_48;
do *str-- += '0'; while (--digcnt);
}