@ -87,72 +87,117 @@ void muls64 (uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b)
}
}
/*
/*
* Unsigned 128 - by - 64 division . Returns quotient via plow and
* Unsigned 128 - by - 64 division .
* remainder via phigh .
* Returns the remainder .
* The result must fit in 64 bits ( plow ) - otherwise , the result
* Returns quotient via plow and phigh .
* is undefined .
* Also returns the remainder via the function return value .
* This function will cause a division by zero if passed a zero divisor .
*/
*/
void divu128 ( uint64_t * plow , uint64_t * phigh , uint64_t divisor )
uint64_t divu128 ( uint64_t * plow , uint64_t * phigh , uint64_t divisor )
{
{
uint64_t dhi = * phigh ;
uint64_t dhi = * phigh ;
uint64_t dlo = * plow ;
uint64_t dlo = * plow ;
unsigned i ;
uint64_t rem , dhighest ;
uint64_t carry = 0 ;
int sh ;
if ( divisor = = 0 | | dhi = = 0 ) {
if ( divisor = = 0 | | dhi = = 0 ) {
* plow = dlo / divisor ;
* plow = dlo / divisor ;
* phigh = dlo % divisor ;
* phigh = 0 ;
return dlo % divisor ;
} else {
} else {
sh = clz64 ( divisor ) ;
for ( i = 0 ; i < 64 ; i + + ) {
if ( dhi < divisor ) {
carry = dhi > > 63 ;
if ( sh ! = 0 ) {
dhi = ( dhi < < 1 ) | ( dlo > > 63 ) ;
/* normalize the divisor, shifting the dividend accordingly */
if ( carry | | ( dhi > = divisor ) ) {
divisor < < = sh ;
dhi - = divisor ;
dhi = ( dhi < < sh ) | ( dlo > > ( 64 - sh ) ) ;
carry = 1 ;
dlo < < = sh ;
}
* phigh = 0 ;
* plow = udiv_qrnnd ( & rem , dhi , dlo , divisor ) ;
} else {
if ( sh ! = 0 ) {
/* normalize the divisor, shifting the dividend accordingly */
divisor < < = sh ;
dhighest = dhi > > ( 64 - sh ) ;
dhi = ( dhi < < sh ) | ( dlo > > ( 64 - sh ) ) ;
dlo < < = sh ;
* phigh = udiv_qrnnd ( & dhi , dhighest , dhi , divisor ) ;
} else {
} else {
carry = 0 ;
/**
* dhi > = divisor
* Since the MSB of divisor is set ( sh = = 0 ) ,
* ( dhi - divisor ) < divisor
*
* Thus , the high part of the quotient is 1 , and we can
* calculate the low part with a single call to udiv_qrnnd
* after subtracting divisor from dhi
*/
dhi - = divisor ;
* phigh = 1 ;
}
}
dlo = ( dlo < < 1 ) | carry ;
* plow = udiv_qrnnd ( & rem , dhi , dlo , divisor ) ;
}
}
* plow = dlo ;
/*
* phigh = dhi ;
* since the dividend / divisor might have been normalized ,
* the remainder might also have to be shifted back
*/
return rem > > sh ;
}
}
}
}
/*
/*
* Signed 128 - by - 64 division . Returns quotient via plow and
* Signed 128 - by - 64 division .
* remainder via phigh .
* Returns quotient via plow and phigh .
* The result must fit in 64 bits ( plow ) - otherwise , the result
* Also returns the remainder via the function return value .
* is undefined .
* This function will cause a division by zero if passed a zero divisor .
*/
*/
void divs128 ( int64_t * plow , int64_t * phigh , int64_t divisor )
int64_t divs128 ( uint64_t * plow , int64_t * phigh , int64_t divisor )
{
{
int sgn_dvdnd = * phigh < 0 ;
bool neg_quotient = false , neg_remainder = false ;
int sgn_divsr = divisor < 0 ;
uint64_t unsig_hi = * phigh , unsig_lo = * plow ;
uint64_t rem ;
if ( sgn_dvdnd ) {
if ( * phigh < 0 ) {
* plow = ~ ( * plow ) ;
neg_quotient = ! neg_quotient ;
* phigh = ~ ( * phigh ) ;
neg_remainder = ! neg_remainder ;
if ( * plow = = ( int64_t ) - 1 ) {
* plow = 0 ;
if ( unsig_lo = = 0 ) {
( * phigh ) + + ;
unsig_hi = - unsig_hi ;
} else {
} else {
( * plow ) + + ;
unsig_hi = ~ unsig_hi ;
}
unsig_lo = - unsig_lo ;
}
}
}
if ( sgn_divsr ) {
if ( divisor < 0 ) {
divisor = 0 - divisor ;
neg_quotient = ! neg_quotient ;
divisor = - divisor ;
}
}
divu128 ( ( uint64_t * ) plow , ( uint64_t * ) phigh , ( uint64_t ) divisor ) ;
rem = divu128 ( & unsig_lo , & unsig_hi , ( uint64_t ) divisor ) ;
if ( neg_quotient ) {
if ( unsig_lo = = 0 ) {
* phigh = - unsig_hi ;
* plow = 0 ;
} else {
* phigh = ~ unsig_hi ;
* plow = - unsig_lo ;
}
} else {
* phigh = unsig_hi ;
* plow = unsig_lo ;
}
if ( sgn_dvdnd ^ sgn_divsr ) {
if ( neg_remainder ) {
* plow = 0 - * plow ;
return - rem ;
} else {
return rem ;
}
}
}
}
# endif
# endif