shithub: femtolisp

ref: a69ae5ed2a33e8d1a62391e57347285228ba4054
dir: /3rd/mp/mptod.c/

View raw version
#include "platform.h"

extern double D_PINF, D_NINF;

double
mptod(mpint *a)
{
	uint64_t v;
	mpdigit w, r;
	int sf, i, n, m, s;
	FPdbleword x;

	if(a->top == 0) return 0.0;
	sf = (int)mpsignif(a);
	if(sf > 1024) return a->sign < 0 ? D_NINF : D_PINF;
	i = a->top - 1;
	v = a->p[i];
	n = sf & Dbits - 1;
	n |= n - 1 & Dbits;
	r = 0;
	if(n > 54){
		s = n - 54;
		r = v & (1ULL<<s) - 1;
		v >>= s;
	}
	while(n < 54){
		if(--i < 0)
			w = 0;
		else
			w = a->p[i];
		m = 54 - n;
		if(m > Dbits) m = Dbits;
		s = Dbits - m & Dbits - 1;
		v = v << m | w >> s;
		r = w & (1ULL<<s) - 1;
		n += m;
	}
	if((v & 3) == 1){
		while(--i >= 0)
			r |= a->p[i];
		if(r != 0)
			v++;
	}else
		v++;
	v >>= 1;
	while((v >> 53) != 0){
		v >>= 1;
		if(++sf > 1024)
			return a->sign < 0 ? D_NINF : D_PINF;
	}
	x.lo = v;
	x.hi = (uint32_t)(v >> 32) & (1<<20) - 1 | (sf + 1022) << 20 | a->sign & 1U<<31;
	return x.x;
}

mpint *
dtomp(double d, mpint *a)
{
	FPdbleword x;
	uint64_t v;
	int e;

	if(a == nil)
		a = mpnew(0);
	x.x = d;
	e = x.hi >> 20 & 2047;
	assert(e != 2047);
	if(e < 1022){
		mpassign(mpzero, a);
		return a;
	}
	v = x.lo | (uint64_t)(x.hi & (1<<20) - 1) << 32 | 1ULL<<52;
	if(e < 1075){
		v += (1ULL<<(1074 - e)) - (~v >> (1075 - e) & 1);
		v >>= 1075 - e;
	}
	uvtomp(v, a);
	if(e > 1075)
		mpleft(a, e - 1075, a);
	if((int)x.hi < 0)
		a->sign = -1;
	return a;
}