% Macros for dealing with very large and very small numbers in `Mlog form'.
% A number x in Mlog form represents mexp(x) if x is an even multiple of
% epsilon and -mexp(x) if x is an odd multiple of epsilon, where epsilon=1/65536
% is the basic unit for MetaPost's fixpoint numbers. Such numbers can represent
% values large as 3.8877e+55 or as small as 1.604e-28. (Anything less than that
% is treated as zero.)
% Mlog_str <string> convert a string like "6.02e23" to Mlog form
% Mexp_str <M_numeric> convert from Mlog form to a string like "6.02e23"
% Meform(q) find (x,y) such that q is the Mlog form of x*10^y
% Mlog <numeric> convert a number to Mlog form
% Mexp <M_numeric> convert from Mlog form into ordinary numeric form
% Mabs <M_numeric> absolute value
% Mlog_Str <string or numeric> convert to Mlog form; unknown results in unknown
% <M_numeric> Madd <M_numeric> add
% <M_numeric> Msub <M_numeric> subtract
% <M_numeric> Mmul <M_numeric> multiply
% <M_numeric> Mdiv <M_numeric> divide
% <M_numeric> Mleq <M_numeric> the boolean operator <=
% Mzero constant that represents zero
% Mten constant that represents 10.0
% All other externally visible names start with `M' and end with `_'.
warningcheck := 0; % Need warningcheck:=0 anytime these macros are in use
if unknown Mzero: % If we have not already seen this file
newinternal Mzero;
Mzero := -16384; % Anything at least this small is treated as zero
input string.mp
% Ideal value of mlog(10) is 589.4617838 or 38630967.46/65536. To get an even
% numerator, we round to 38630968/65536 or 589.4617920.
newinternal Mten;
Mten := 589.46179;
% Convert x*10^y to Mlog form, assuming x is already in Mlog form.
primarydef x M_e_ y = (x+Mten*y) enddef;
def Mgobble_ text t = enddef;
pair M_iv_; M_iv_=(0,4);
% String s is a number in scientific notatation with no leading spaces.
% Convert it to Mlog form.
vardef Mlog_str primary s =
save k, t, e, r;
string t;
if substring(0,1) of s="-":
r=-epsilon; t=substring (1,infinity) of s;
else:
r=0; t=s;
fi
let e = Mgobble_;
if begingroup scantokens substring M_iv_ of t endgroup >= 1000:
k = cspan(t,isdigit);
t := substring M_iv_ of t & "." & substring(4,k) of t &
substring (k if substring(k,k+1) of t=".": +1 fi, infinity) of t;
r := r + (k-4)*Mten;
fi
let e = M_e_;
r + Mlog scantokens t
enddef;
% Convert x from Mlog form into a pair whose xpart gives a mantissa and whose
% ypart gives a power of ten. The constant 1768.38985 is slightly more than
% 3Mten as is required to ensure that 3Mten-epsilon<=q+e*Mten<4Mten-epsilon.
% This forces the xpart of the result to be between 1000 and 10000.
vardef Meform(expr q) =
if q<=Mzero: (0,0)
else:
save e; e=floor((q-1768.38985)/Mten);
(Mexp(q-e*Mten), e)
fi
enddef;
% Perform the inverse of Mlog_str, converting from Mlog form to a string.
vardef Mexp_str primary x =
save p;
pair p; p=Meform(x);
decimal xpart p
if ypart p<>0: & "e" & decimal ypart p fi
enddef;
vardef Mabs primary x = x*.5*2 enddef;
% Convert a number to Mlog form
vardef Mlog primary x =
if x>0: Mabs mlog x
elseif x<0: epsilon + Mabs mlog(-x)
else: Mzero
fi
enddef;
% Convert a number from Mlog form
vardef Mexp primary x =
if x=Mabs x: mexp x
else: -mexp x
fi
enddef;
primarydef a Mmul b =
if (a<=Mzero) or (b<=Mzero): Mzero
else: (a+b)
fi
enddef;
primarydef a Mdiv b =
if a<=Mzero: Mzero
else: (a-b)
fi
enddef;
% 256ln(1579)=123556596.0003/65536=1885.3240356445
secondarydef a Madd b =
if a>=b: (Mlog(1579 + Mexp(b Mmul (1885.32404-a))) + a-1885.32404)
else: b Madd a
fi
enddef;
secondarydef a Msub b = a Madd (b-epsilon) enddef;
tertiarydef a Mleq b =
(if a=Mabs a:
if b=Mabs b: a<=b else: false fi
elseif b=Mabs b: true else: b<=a fi
)
enddef;
vardef Mlog_Str primary x =
if unknown x: whatever
elseif string x: Mlog_str x
else: Mlog x
fi
enddef;
fi % end the if..fi that encloses this file
|