URL
https://opencores.org/ocsvn/eco32/eco32/trunk
Subversion Repositories eco32
Compare Revisions
- This comparison shows the changes necessary to convert path
/eco32/tags/eco32-0.22/fp/implementation/arith
- from Rev 15 to Rev 21
- ↔ Reverse comparison
Rev 15 → Rev 21
/mmix-arith.c
0,0 → 1,1670
#define sign_bit ((unsigned) 0x80000000) \ |
|
#define ROUND_OFF 1 |
#define ROUND_UP 2 |
#define ROUND_DOWN 3 |
#define ROUND_NEAR 4 \ |
|
#define X_BIT (1<<8) |
#define Z_BIT (1<<9) |
#define U_BIT (1<<10) |
#define O_BIT (1<<11) |
#define I_BIT (1<<12) |
#define W_BIT (1<<13) |
#define V_BIT (1<<14) |
#define D_BIT (1<<15) |
#define E_BIT (1<<18) \ |
|
#define zero_exponent (-1000) \ |
|
#define bignum_prec 157 \ |
|
#define magic_offset 2112 |
#define origin 37 \ |
|
#define buf0 (buf+8) |
#define buf_max (buf+777) \ |
|
/*1:*/ |
#line 32 "./mmix-arith.w" |
|
#include <stdio.h> |
#include <string.h> |
#include <ctype.h> |
/*2:*/ |
#line 49 "./mmix-arith.w" |
|
#ifdef __STDC__ |
#define ARGS(list) list |
#else |
#define ARGS(list) () |
#endif |
|
/*:2*/ |
#line 36 "./mmix-arith.w" |
|
typedef enum{false,true}bool; |
/*3:*/ |
#line 60 "./mmix-arith.w" |
|
typedef unsigned int tetra; |
|
typedef struct{tetra h,l;}octa; |
|
/*:3*/ |
#line 38 "./mmix-arith.w" |
|
/*36:*/ |
#line 605 "./mmix-arith.w" |
|
typedef enum{zro,num,inf,nan}ftype; |
|
/*:36*//*59:*/ |
#line 1110 "./mmix-arith.w" |
|
typedef struct{ |
int a; |
int b; |
tetra dat[bignum_prec]; |
}bignum; |
|
/*:59*/ |
#line 39 "./mmix-arith.w" |
|
/*4:*/ |
#line 67 "./mmix-arith.w" |
|
octa zero_octa; |
octa neg_one= {-1,-1}; |
octa inf_octa= {0x7ff00000,0}; |
octa standard_NaN= {0x7ff80000,0}; |
octa aux; |
bool overflow; |
|
/*:4*//*9:*/ |
#line 174 "./mmix-arith.w" |
|
extern octa aux; |
extern bool overflow; |
|
/*:9*//*30:*/ |
#line 464 "./mmix-arith.w" |
|
int cur_round; |
|
/*:30*//*32:*/ |
#line 528 "./mmix-arith.w" |
|
int exceptions; |
|
/*:32*//*69:*/ |
#line 1359 "./mmix-arith.w" |
|
octa val; |
char*next_char; |
|
/*:69*//*75:*/ |
#line 1432 "./mmix-arith.w" |
|
static char buf[785]= "00000000"; |
|
/*:75*/ |
#line 40 "./mmix-arith.w" |
|
/*5:*/ |
#line 78 "./mmix-arith.w" |
|
octa oplus ARGS((octa,octa)); |
octa oplus(y,z) |
octa y,z; |
{octa x; |
x.h= y.h+z.h; |
x.l= y.l+z.l; |
if(x.l<y.l)x.h++; |
return x; |
} |
|
octa ominus ARGS((octa,octa)); |
octa ominus(y,z) |
octa y,z; |
{octa x; |
x.h= y.h-z.h; |
x.l= y.l-z.l; |
if(x.l> y.l)x.h--; |
return x; |
} |
|
/*:5*//*6:*/ |
#line 102 "./mmix-arith.w" |
|
octa incr ARGS((octa,int)); |
octa incr(y,delta) |
octa y; |
int delta; |
{octa x; |
x.h= y.h;x.l= y.l+delta; |
if(delta>=0){ |
if(x.l<y.l)x.h++; |
}else if(x.l> y.l)x.h--; |
return x; |
} |
|
/*:6*//*7:*/ |
#line 117 "./mmix-arith.w" |
|
octa shift_left ARGS((octa,int)); |
octa shift_left(y,s) |
octa y; |
int s; |
{ |
while(s>=32)y.h= y.l,y.l= 0,s-= 32; |
if(s){register tetra yhl= y.h<<s,ylh= y.l>>(32-s); |
y.h= yhl+ylh;y.l<<= s; |
} |
return y; |
} |
|
octa shift_right ARGS((octa,int,int)); |
octa shift_right(y,s,u) |
octa y; |
int s,u; |
{ |
while(s>=32)y.l= y.h,y.h= (u?0:-(y.h>>31)),s-= 32; |
if(s){register tetra yhl= y.h<<(32-s),ylh= y.l>>s; |
y.h= (u?0:(-(y.h>>31))<<(32-s))+(y.h>>s);y.l= yhl+ylh; |
} |
return y; |
} |
|
/*:7*//*8:*/ |
#line 150 "./mmix-arith.w" |
|
octa omult ARGS((octa,octa)); |
octa omult(y,z) |
octa y,z; |
{ |
register int i,j,k; |
tetra u[4],v[4],w[8]; |
register tetra t; |
octa acc; |
/*10:*/ |
#line 178 "./mmix-arith.w" |
|
u[3]= y.h>>16,u[2]= y.h&0xffff,u[1]= y.l>>16,u[0]= y.l&0xffff; |
v[3]= z.h>>16,v[2]= z.h&0xffff,v[1]= z.l>>16,v[0]= z.l&0xffff; |
|
/*:10*/ |
#line 159 "./mmix-arith.w" |
; |
for(j= 0;j<4;j++)w[j]= 0; |
for(j= 0;j<4;j++) |
if(!v[j])w[j+4]= 0; |
else{ |
for(i= k= 0;i<4;i++){ |
t= u[i]*v[j]+w[i+j]+k; |
w[i+j]= t&0xffff,k= t>>16; |
} |
w[j+4]= k; |
} |
/*11:*/ |
#line 182 "./mmix-arith.w" |
|
aux.h= (w[7]<<16)+w[6],aux.l= (w[5]<<16)+w[4]; |
acc.h= (w[3]<<16)+w[2],acc.l= (w[1]<<16)+w[0]; |
|
/*:11*/ |
#line 170 "./mmix-arith.w" |
; |
return acc; |
} |
|
/*:8*//*12:*/ |
#line 191 "./mmix-arith.w" |
|
octa signed_omult ARGS((octa,octa)); |
octa signed_omult(y,z) |
octa y,z; |
{ |
octa acc; |
acc= omult(y,z); |
if(y.h&sign_bit)aux= ominus(aux,z); |
if(z.h&sign_bit)aux= ominus(aux,y); |
overflow= (aux.h!=aux.l||(aux.h^(aux.h>>1)^(acc.h&sign_bit))); |
return acc; |
} |
|
/*:12*//*13:*/ |
#line 215 "./mmix-arith.w" |
|
octa odiv ARGS((octa,octa,octa)); |
octa odiv(x,y,z) |
octa x,y,z; |
{ |
register int i,j,k,n,d; |
tetra u[8],v[4],q[4],mask,qhat,rhat,vh,vmh; |
register tetra t; |
octa acc; |
/*14:*/ |
#line 234 "./mmix-arith.w" |
|
if(x.h> z.h||(x.h==z.h&&x.l>=z.l)){ |
aux= y;return x; |
} |
|
/*:14*/ |
#line 224 "./mmix-arith.w" |
; |
/*15:*/ |
#line 239 "./mmix-arith.w" |
|
u[7]= x.h>>16,u[6]= x.h&0xffff,u[5]= x.l>>16,u[4]= x.l&0xffff; |
u[3]= y.h>>16,u[2]= y.h&0xffff,u[1]= y.l>>16,u[0]= y.l&0xffff; |
v[3]= z.h>>16,v[2]= z.h&0xffff,v[1]= z.l>>16,v[0]= z.l&0xffff; |
|
/*:15*/ |
#line 225 "./mmix-arith.w" |
; |
/*16:*/ |
#line 244 "./mmix-arith.w" |
|
for(n= 4;v[n-1]==0;n--); |
|
/*:16*/ |
#line 226 "./mmix-arith.w" |
; |
/*17:*/ |
#line 250 "./mmix-arith.w" |
|
vh= v[n-1]; |
for(d= 0;vh<0x8000;d++,vh<<= 1); |
for(j= k= 0;j<n+4;j++){ |
t= (u[j]<<d)+k; |
u[j]= t&0xffff,k= t>>16; |
} |
for(j= k= 0;j<n;j++){ |
t= (v[j]<<d)+k; |
v[j]= t&0xffff,k= t>>16; |
} |
vh= v[n-1]; |
vmh= (n> 1?v[n-2]:0); |
|
/*:17*/ |
#line 227 "./mmix-arith.w" |
; |
for(j= 3;j>=0;j--)/*20:*/ |
#line 276 "./mmix-arith.w" |
|
{ |
/*21:*/ |
#line 284 "./mmix-arith.w" |
|
t= (u[j+n]<<16)+u[j+n-1]; |
qhat= t/vh,rhat= t-vh*qhat; |
if(n> 1)while(qhat==0x10000||qhat*vmh> (rhat<<16)+u[j+n-2]){ |
qhat--,rhat+= vh; |
if(rhat>=0x10000)break; |
} |
|
/*:21*/ |
#line 278 "./mmix-arith.w" |
; |
/*22:*/ |
#line 296 "./mmix-arith.w" |
|
for(i= k= 0;i<n;i++){ |
t= u[i+j]+0xffff0000-k-qhat*v[i]; |
u[i+j]= t&0xffff,k= 0xffff-(t>>16); |
} |
|
/*:22*/ |
#line 279 "./mmix-arith.w" |
; |
/*23:*/ |
#line 305 "./mmix-arith.w" |
|
if(u[j+n]!=k){ |
qhat--; |
for(i= k= 0;i<n;i++){ |
t= u[i+j]+v[i]+k; |
u[i+j]= t&0xffff,k= t>>16; |
} |
} |
|
/*:23*/ |
#line 280 "./mmix-arith.w" |
; |
q[j]= qhat; |
} |
|
/*:20*/ |
#line 228 "./mmix-arith.w" |
; |
/*18:*/ |
#line 264 "./mmix-arith.w" |
|
mask= (1<<d)-1; |
for(j= 3;j>=n;j--)u[j]= 0; |
for(k= 0;j>=0;j--){ |
t= (k<<16)+u[j]; |
u[j]= t>>d,k= t&mask; |
} |
|
/*:18*/ |
#line 229 "./mmix-arith.w" |
; |
/*19:*/ |
#line 272 "./mmix-arith.w" |
|
acc.h= (q[3]<<16)+q[2],acc.l= (q[1]<<16)+q[0]; |
aux.h= (u[3]<<16)+u[2],aux.l= (u[1]<<16)+u[0]; |
|
/*:19*/ |
#line 230 "./mmix-arith.w" |
; |
return acc; |
} |
|
/*:13*//*24:*/ |
#line 317 "./mmix-arith.w" |
|
octa signed_odiv ARGS((octa,octa)); |
octa signed_odiv(y,z) |
octa y,z; |
{ |
octa yy,zz,q; |
register int sy,sz; |
if(y.h&sign_bit)sy= 2,yy= ominus(zero_octa,y); |
else sy= 0,yy= y; |
if(z.h&sign_bit)sz= 1,zz= ominus(zero_octa,z); |
else sz= 0,zz= z; |
q= odiv(zero_octa,yy,zz); |
overflow= false; |
switch(sy+sz){ |
case 2+1:aux= ominus(zero_octa,aux); |
if(q.h==sign_bit)overflow= true; |
case 0+0:return q; |
case 2+0:if(aux.h||aux.l)aux= ominus(zz,aux); |
goto negate_q; |
case 0+1:if(aux.h||aux.l)aux= ominus(aux,zz); |
negate_q:if(aux.h||aux.l)return ominus(neg_one,q); |
else return ominus(zero_octa,q); |
} |
} |
|
/*:24*//*25:*/ |
#line 346 "./mmix-arith.w" |
|
octa oand ARGS((octa,octa)); |
octa oand(y,z) |
octa y,z; |
{octa x; |
x.h= y.h&z.h;x.l= y.l&z.l; |
return x; |
} |
|
octa oandn ARGS((octa,octa)); |
octa oandn(y,z) |
octa y,z; |
{octa x; |
x.h= y.h&~z.h;x.l= y.l&~z.l; |
return x; |
} |
|
octa oxor ARGS((octa,octa)); |
octa oxor(y,z) |
octa y,z; |
{octa x; |
x.h= y.h^z.h;x.l= y.l^z.l; |
return x; |
} |
|
/*:25*//*26:*/ |
#line 387 "./mmix-arith.w" |
|
int count_bits ARGS((tetra)); |
int count_bits(x) |
tetra x; |
{ |
register int xx= x; |
xx= xx-((xx>>1)&0x55555555); |
xx= (xx&0x33333333)+((xx>>2)&0x33333333); |
xx= (xx+(xx>>4))&0x0f0f0f0f; |
xx= xx+(xx>>8); |
return(xx+(xx>>16))&0xff; |
} |
|
/*:26*//*27:*/ |
#line 403 "./mmix-arith.w" |
|
tetra byte_diff ARGS((tetra,tetra)); |
tetra byte_diff(y,z) |
tetra y,z; |
{ |
register tetra d= (y&0x00ff00ff)+0x01000100-(z&0x00ff00ff); |
register tetra m= d&0x01000100; |
register tetra x= d&(m-(m>>8)); |
d= ((y>>8)&0x00ff00ff)+0x01000100-((z>>8)&0x00ff00ff); |
m= d&0x01000100; |
return x+((d&(m-(m>>8)))<<8); |
} |
|
/*:27*//*28:*/ |
#line 421 "./mmix-arith.w" |
|
tetra wyde_diff ARGS((tetra,tetra)); |
tetra wyde_diff(y,z) |
tetra y,z; |
{ |
register tetra a= ((y>>16)-(z>>16))&0x10000; |
register tetra b= ((y&0xffff)-(z&0xffff))&0x10000; |
return y-(z^((y^z)&(b-a-(b>>16)))); |
} |
|
/*:28*//*29:*/ |
#line 434 "./mmix-arith.w" |
|
octa bool_mult ARGS((octa,octa,bool)); |
octa bool_mult(y,z,xor) |
octa y,z; |
bool xor; |
{ |
octa o,x; |
register tetra a,b,c; |
register int k; |
for(k= 0,o= y,x= zero_octa;o.h||o.l;k++,o= shift_right(o,8,1)) |
if(o.l&0xff){ |
a= ((z.h>>k)&0x01010101)*0xff; |
b= ((z.l>>k)&0x01010101)*0xff; |
c= (o.l&0xff)*0x01010101; |
if(xor)x.h^= a&c,x.l^= b&c; |
else x.h|= a&c,x.l|= b&c; |
} |
return x; |
} |
|
/*:29*//*31:*/ |
#line 503 "./mmix-arith.w" |
|
octa fpack ARGS((octa,int,char,int)); |
octa fpack(f,e,s,r) |
octa f; |
int e; |
char s; |
int r; |
{ |
octa o; |
if(e> 0x7fd)e= 0x7ff,o= zero_octa; |
else{ |
if(e<0){ |
if(e<-54)o.h= 0,o.l= 1; |
else{octa oo; |
o= shift_right(f,-e,1); |
oo= shift_left(o,-e); |
if(oo.l!=f.l||oo.h!=f.h)o.l|= 1; |
|
} |
e= 0; |
}else o= f; |
} |
/*33:*/ |
#line 533 "./mmix-arith.w" |
|
if(o.l&3)exceptions|= X_BIT; |
switch(r){ |
case ROUND_DOWN:if(s=='-')o= incr(o,3);break; |
case ROUND_UP:if(s!='-')o= incr(o,3); |
case ROUND_OFF:break; |
case ROUND_NEAR:o= incr(o,o.l&4?2:1);break; |
} |
o= shift_right(o,2,1); |
o.h+= e<<20; |
if(o.h>=0x7ff00000)exceptions|= O_BIT+X_BIT; |
else if(o.h<0x100000)exceptions|= U_BIT; |
if(s=='-')o.h|= sign_bit; |
return o; |
|
/*:33*/ |
#line 525 "./mmix-arith.w" |
; |
} |
|
/*:31*//*34:*/ |
#line 551 "./mmix-arith.w" |
|
tetra sfpack ARGS((octa,int,char,int)); |
tetra sfpack(f,e,s,r) |
octa f; |
int e; |
char s; |
int r; |
{ |
register tetra o; |
if(e> 0x47d)e= 0x47f,o= 0; |
else{ |
o= shift_left(f,3).h; |
if(f.l&0x1fffffff)o|= 1; |
if(e<0x380){ |
if(e<0x380-25)o= 1; |
else{register tetra o0,oo; |
o0= o; |
o= o>>(0x380-e); |
oo= o<<(0x380-e); |
if(oo!=o0)o|= 1; |
|
} |
e= 0x380; |
} |
} |
/*35:*/ |
#line 579 "./mmix-arith.w" |
|
if(o&3)exceptions|= X_BIT; |
switch(r){ |
case ROUND_DOWN:if(s=='-')o+= 3;break; |
case ROUND_UP:if(s!='-')o+= 3; |
case ROUND_OFF:break; |
case ROUND_NEAR:o+= (o&4?2:1);break; |
} |
o= o>>2; |
o+= (e-0x380)<<23; |
if(o>=0x7f800000)exceptions|= O_BIT+X_BIT; |
else if(o<0x100000)exceptions|= U_BIT; |
if(s=='-')o|= sign_bit; |
return o; |
|
/*:35*/ |
#line 576 "./mmix-arith.w" |
; |
} |
|
/*:34*//*37:*/ |
#line 608 "./mmix-arith.w" |
|
ftype funpack ARGS((octa,octa*,int*,char*)); |
ftype funpack(x,f,e,s) |
octa x; |
octa*f; |
int*e; |
char*s; |
{ |
register int ee; |
exceptions= 0; |
*s= (x.h&sign_bit?'-':'+'); |
*f= shift_left(x,2); |
f->h&= 0x3fffff; |
ee= (x.h>>20)&0x7ff; |
if(ee){ |
*e= ee-1; |
f->h|= 0x400000; |
return(ee<0x7ff?num:f->h==0x400000&&!f->l?inf:nan); |
} |
if(!x.l&&!f->h){ |
*e= zero_exponent;return zro; |
} |
do{ee--;*f= shift_left(*f,1);}while(!(f->h&0x400000)); |
*e= ee;return num; |
} |
|
/*:37*//*38:*/ |
#line 634 "./mmix-arith.w" |
|
ftype sfunpack ARGS((tetra,octa*,int*,char*)); |
ftype sfunpack(x,f,e,s) |
tetra x; |
octa*f; |
int*e; |
char*s; |
{ |
register int ee; |
exceptions= 0; |
*s= (x&sign_bit?'-':'+'); |
f->h= (x>>1)&0x3fffff,f->l= x<<31; |
ee= (x>>23)&0xff; |
if(ee){ |
*e= ee+0x380-1; |
f->h|= 0x400000; |
return(ee<0xff?num:(x&0x7fffffff)==0x7f800000?inf:nan); |
} |
if(!(x&0x7fffffff)){ |
*e= zero_exponent;return zro; |
} |
do{ee--;*f= shift_left(*f,1);}while(!(f->h&0x400000)); |
*e= ee+0x380;return num; |
} |
|
/*:38*//*39:*/ |
#line 663 "./mmix-arith.w" |
|
octa load_sf ARGS((tetra)); |
octa load_sf(z) |
tetra z; |
{ |
octa f,x;int e;char s;ftype t; |
t= sfunpack(z,&f,&e,&s); |
switch(t){ |
case zro:x= zero_octa;break; |
case num:return fpack(f,e,s,ROUND_OFF); |
case inf:x= inf_octa;break; |
case nan:x= shift_right(f,2,1);x.h|= 0x7ff00000;break; |
} |
if(s=='-')x.h|= sign_bit; |
return x; |
} |
|
/*:39*//*40:*/ |
#line 680 "./mmix-arith.w" |
|
tetra store_sf ARGS((octa)); |
tetra store_sf(x) |
octa x; |
{ |
octa f;tetra z;int e;char s;ftype t; |
t= funpack(x,&f,&e,&s); |
switch(t){ |
case zro:z= 0;break; |
case num:return sfpack(f,e,s,cur_round); |
case inf:z= 0x7f800000;break; |
case nan:if(!(f.h&0x200000)){ |
f.h|= 0x200000;exceptions|= I_BIT; |
} |
z= 0x7f800000|(f.h<<1)|(f.l>>31);break; |
} |
if(s=='-')z|= sign_bit; |
return z; |
} |
|
/*:40*//*41:*/ |
#line 705 "./mmix-arith.w" |
|
octa fmult ARGS((octa,octa)); |
octa fmult(y,z) |
octa y,z; |
{ |
ftype yt,zt; |
int ye,ze; |
char ys,zs; |
octa x,xf,yf,zf; |
register int xe; |
register char xs; |
yt= funpack(y,&yf,&ye,&ys); |
zt= funpack(z,&zf,&ze,&zs); |
xs= ys+zs-'+'; |
switch(4*yt+zt){ |
/*42:*/ |
#line 731 "./mmix-arith.w" |
|
case 4*nan+nan:if(!(y.h&0x80000))exceptions|= I_BIT; |
case 4*zro+nan:case 4*num+nan:case 4*inf+nan: |
if(!(z.h&0x80000))exceptions|= I_BIT,z.h|= 0x80000; |
return z; |
case 4*nan+zro:case 4*nan+num:case 4*nan+inf: |
if(!(y.h&0x80000))exceptions|= I_BIT,y.h|= 0x80000; |
return y; |
|
/*:42*/ |
#line 720 "./mmix-arith.w" |
; |
case 4*zro+zro:case 4*zro+num:case 4*num+zro:x= zero_octa;break; |
case 4*num+inf:case 4*inf+num:case 4*inf+inf:x= inf_octa;break; |
case 4*zro+inf:case 4*inf+zro:x= standard_NaN; |
exceptions|= I_BIT;break; |
case 4*num+num:/*43:*/ |
#line 740 "./mmix-arith.w" |
|
xe= ye+ze-0x3fd; |
x= omult(yf,shift_left(zf,9)); |
if(aux.h>=0x400000)xf= aux; |
else xf= shift_left(aux,1),xe--; |
if(x.h||x.l)xf.l|= 1; |
return fpack(xf,xe,xs,cur_round); |
|
/*:43*/ |
#line 725 "./mmix-arith.w" |
; |
} |
if(xs=='-')x.h|= sign_bit; |
return x; |
} |
|
/*:41*//*44:*/ |
#line 748 "./mmix-arith.w" |
|
octa fdivide ARGS((octa,octa)); |
octa fdivide(y,z) |
octa y,z; |
{ |
ftype yt,zt; |
int ye,ze; |
char ys,zs; |
octa x,xf,yf,zf; |
register int xe; |
register char xs; |
yt= funpack(y,&yf,&ye,&ys); |
zt= funpack(z,&zf,&ze,&zs); |
xs= ys+zs-'+'; |
switch(4*yt+zt){ |
/*42:*/ |
#line 731 "./mmix-arith.w" |
|
case 4*nan+nan:if(!(y.h&0x80000))exceptions|= I_BIT; |
case 4*zro+nan:case 4*num+nan:case 4*inf+nan: |
if(!(z.h&0x80000))exceptions|= I_BIT,z.h|= 0x80000; |
return z; |
case 4*nan+zro:case 4*nan+num:case 4*nan+inf: |
if(!(y.h&0x80000))exceptions|= I_BIT,y.h|= 0x80000; |
return y; |
|
/*:42*/ |
#line 763 "./mmix-arith.w" |
; |
case 4*zro+inf:case 4*zro+num:case 4*num+inf:x= zero_octa;break; |
case 4*num+zro:exceptions|= Z_BIT; |
case 4*inf+num:case 4*inf+zro:x= inf_octa;break; |
case 4*zro+zro:case 4*inf+inf:x= standard_NaN; |
exceptions|= I_BIT;break; |
case 4*num+num:/*45:*/ |
#line 775 "./mmix-arith.w" |
|
xe= ye-ze+0x3fd; |
xf= odiv(yf,zero_octa,shift_left(zf,9)); |
if(xf.h>=0x800000){ |
aux.l|= xf.l&1; |
xf= shift_right(xf,1,1); |
xe++; |
} |
if(aux.h||aux.l)xf.l|= 1; |
return fpack(xf,xe,xs,cur_round); |
|
/*:45*/ |
#line 769 "./mmix-arith.w" |
; |
} |
if(xs=='-')x.h|= sign_bit; |
return x; |
} |
|
/*:44*//*46:*/ |
#line 790 "./mmix-arith.w" |
|
octa fplus ARGS((octa,octa)); |
octa fplus(y,z) |
octa y,z; |
{ |
ftype yt,zt; |
int ye,ze; |
char ys,zs; |
octa x,xf,yf,zf; |
register int xe,d; |
register char xs; |
yt= funpack(y,&yf,&ye,&ys); |
zt= funpack(z,&zf,&ze,&zs); |
switch(4*yt+zt){ |
/*42:*/ |
#line 731 "./mmix-arith.w" |
|
case 4*nan+nan:if(!(y.h&0x80000))exceptions|= I_BIT; |
case 4*zro+nan:case 4*num+nan:case 4*inf+nan: |
if(!(z.h&0x80000))exceptions|= I_BIT,z.h|= 0x80000; |
return z; |
case 4*nan+zro:case 4*nan+num:case 4*nan+inf: |
if(!(y.h&0x80000))exceptions|= I_BIT,y.h|= 0x80000; |
return y; |
|
/*:42*/ |
#line 804 "./mmix-arith.w" |
; |
case 4*zro+num:return fpack(zf,ze,zs,ROUND_OFF);break; |
case 4*num+zro:return fpack(yf,ye,ys,ROUND_OFF);break; |
case 4*inf+inf:if(ys!=zs){ |
exceptions|= I_BIT;x= standard_NaN;xs= zs;break; |
} |
case 4*num+inf:case 4*zro+inf:x= inf_octa;xs= zs;break; |
case 4*inf+num:case 4*inf+zro:x= inf_octa;xs= ys;break; |
case 4*num+num:if(y.h!=(z.h^0x80000000)||y.l!=z.l) |
/*47:*/ |
#line 821 "./mmix-arith.w" |
|
{octa o,oo; |
if(ye<ze||(ye==ze&&(yf.h<zf.h||(yf.h==zf.h&&yf.l<zf.l)))) |
/*48:*/ |
#line 839 "./mmix-arith.w" |
|
{ |
o= yf,yf= zf,zf= o; |
d= ye,ye= ze,ze= d; |
d= ys,ys= zs,zs= d; |
} |
|
/*:48*/ |
#line 824 "./mmix-arith.w" |
; |
d= ye-ze; |
xs= ys,xe= ye; |
if(d)/*49:*/ |
#line 859 "./mmix-arith.w" |
|
{ |
if(d<=2)zf= shift_right(zf,d,1); |
else if(d> 53)zf.h= 0,zf.l= 1; |
else{ |
if(ys!=zs)d--,xe--,yf= shift_left(yf,1); |
o= zf; |
zf= shift_right(o,d,1); |
oo= shift_left(zf,d); |
if(oo.l!=o.l||oo.h!=o.h)zf.l|= 1; |
} |
} |
|
/*:49*/ |
#line 827 "./mmix-arith.w" |
; |
if(ys==zs){ |
xf= oplus(yf,zf); |
if(xf.h>=0x800000)xe++,d= xf.l&1,xf= shift_right(xf,1,1),xf.l|= d; |
}else{ |
xf= ominus(yf,zf); |
if(xf.h>=0x800000)xe++,d= xf.l&1,xf= shift_right(xf,1,1),xf.l|= d; |
else while(xf.h<0x400000)xe--,xf= shift_left(xf,1); |
} |
return fpack(xf,xe,xs,cur_round); |
} |
|
/*:47*/ |
#line 813 "./mmix-arith.w" |
; |
case 4*zro+zro:x= zero_octa; |
xs= (ys==zs?ys:cur_round==ROUND_DOWN?'-':'+');break; |
} |
if(xs=='-')x.h|= sign_bit; |
return x; |
} |
|
/*:46*//*50:*/ |
#line 883 "./mmix-arith.w" |
|
int fepscomp ARGS((octa,octa,octa,int)); |
int fepscomp(y,z,e,s) |
octa y,z,e; |
int s; |
{ |
octa yf,zf,ef,o,oo; |
int ye,ze,ee; |
char ys,zs,es; |
register int yt,zt,et,d; |
et= funpack(e,&ef,&ee,&es); |
if(es=='-')return 2; |
switch(et){ |
case nan:return 2; |
case inf:ee= 10000; |
case num:case zro:break; |
} |
yt= funpack(y,&yf,&ye,&ys); |
zt= funpack(z,&zf,&ze,&zs); |
switch(4*yt+zt){ |
case 4*nan+nan:case 4*nan+inf:case 4*nan+num:case 4*nan+zro: |
case 4*inf+nan:case 4*num+nan:case 4*zro+nan:return 2; |
case 4*inf+inf:return(ys==zs||ee>=1023); |
case 4*inf+num:case 4*inf+zro:case 4*num+inf:case 4*zro+inf: |
return(s&&ee>=1022); |
case 4*zro+zro:return 1; |
case 4*zro+num:case 4*num+zro:if(!s)return 0; |
case 4*num+num:break; |
} |
/*51:*/ |
#line 919 "./mmix-arith.w" |
|
/*52:*/ |
#line 934 "./mmix-arith.w" |
|
if(ye<0&&yt!=zro)yf= shift_left(y,2),ye= 0; |
if(ze<0&&zt!=zro)zf= shift_left(z,2),ze= 0; |
|
/*:52*/ |
#line 920 "./mmix-arith.w" |
; |
if(ye<ze||(ye==ze&&(yf.h<zf.h||(yf.h==zf.h&&yf.l<zf.l)))) |
/*48:*/ |
#line 839 "./mmix-arith.w" |
|
{ |
o= yf,yf= zf,zf= o; |
d= ye,ye= ze,ze= d; |
d= ys,ys= zs,zs= d; |
} |
|
/*:48*/ |
#line 922 "./mmix-arith.w" |
; |
if(ze==zero_exponent)ze= ye; |
d= ye-ze; |
if(!s)ee-= d; |
if(ee>=1023)return 1; |
/*53:*/ |
#line 956 "./mmix-arith.w" |
|
if(d> 54)o= zero_octa,oo= zf; |
else o= shift_right(zf,d,1),oo= shift_left(o,d); |
if(oo.h!=zf.h||oo.l!=zf.l){ |
if(ee<1020)return 0; |
o= incr(o,ys==zs?0:1); |
} |
o= (ys==zs?ominus(yf,o):oplus(yf,o)); |
|
/*:53*/ |
#line 927 "./mmix-arith.w" |
; |
if(!o.h&&!o.l)return 1; |
if(ee<968)return 0; |
if(ee>=1021)ef= shift_left(ef,ee-1021); |
else ef= shift_right(ef,1021-ee,1); |
return o.h<ef.h||(o.h==ef.h&&o.l<=ef.l); |
|
/*:51*/ |
#line 912 "./mmix-arith.w" |
; |
} |
|
/*:50*//*54:*/ |
#line 972 "./mmix-arith.w" |
|
static void bignum_times_ten ARGS((bignum*)); |
static void bignum_dec ARGS((bignum*,bignum*,tetra)); |
static int bignum_compare ARGS((bignum*,bignum*)); |
void print_float ARGS((octa)); |
void print_float(x) |
octa x; |
{ |
/*56:*/ |
#line 1035 "./mmix-arith.w" |
|
octa f,g; |
register int e; |
register int j,k; |
|
/*:56*//*66:*/ |
#line 1281 "./mmix-arith.w" |
|
bignum ff,gg; |
bignum tt; |
char s[18]; |
register char*p; |
|
/*:66*/ |
#line 980 "./mmix-arith.w" |
; |
if(x.h&sign_bit)printf("-"); |
/*55:*/ |
#line 1019 "./mmix-arith.w" |
|
f= shift_left(x,1); |
e= f.h>>21; |
f.h&= 0x1fffff; |
if(!f.h&&!f.l)/*57:*/ |
#line 1045 "./mmix-arith.w" |
|
{ |
if(!e){ |
printf("0.");return; |
} |
if(e==0x7ff){ |
printf("Inf");return; |
} |
e--; |
f.h= 0x3fffff,f.l= 0xffffffff; |
g.h= 0x400000,g.l= 2; |
} |
|
/*:57*/ |
#line 1023 "./mmix-arith.w" |
|
else{ |
g= incr(f,1); |
f= incr(f,-1); |
if(!e)e= 1; |
else if(e==0x7ff){ |
printf("NaN"); |
if(g.h==0x100000&&g.l==1)return; |
e= 0x3ff; |
}else f.h|= 0x200000,g.h|= 0x200000; |
} |
|
/*:55*/ |
#line 983 "./mmix-arith.w" |
; |
/*63:*/ |
#line 1195 "./mmix-arith.w" |
|
k= (magic_offset-e)/28; |
ff.dat[k-1]= shift_right(f,magic_offset+28-e-28*k,1).l&0xfffffff; |
gg.dat[k-1]= shift_right(g,magic_offset+28-e-28*k,1).l&0xfffffff; |
ff.dat[k]= shift_right(f,magic_offset-e-28*k,1).l&0xfffffff; |
gg.dat[k]= shift_right(g,magic_offset-e-28*k,1).l&0xfffffff; |
ff.dat[k+1]= shift_left(f,e+28*k-(magic_offset-28)).l&0xfffffff; |
gg.dat[k+1]= shift_left(g,e+28*k-(magic_offset-28)).l&0xfffffff; |
ff.a= (ff.dat[k-1]?k-1:k); |
ff.b= (ff.dat[k+1]?k+1:k); |
gg.a= (gg.dat[k-1]?k-1:k); |
gg.b= (gg.dat[k+1]?k+1:k); |
|
/*:63*/ |
#line 984 "./mmix-arith.w" |
; |
/*64:*/ |
#line 1223 "./mmix-arith.w" |
|
if(e> 0x401)/*65:*/ |
#line 1254 "./mmix-arith.w" |
|
{register int open= x.l&1; |
tt.dat[origin]= 10; |
tt.a= tt.b= origin; |
for(e= 1;bignum_compare(&gg,&tt)>=open;e++) |
bignum_times_ten(&tt); |
p= s; |
while(1){ |
bignum_times_ten(&ff); |
bignum_times_ten(&gg); |
for(j= '0';bignum_compare(&ff,&tt)>=0;j++) |
bignum_dec(&ff,&tt,0x10000000),bignum_dec(&gg,&tt,0x10000000); |
if(bignum_compare(&gg,&tt)>=open)break; |
*p++= j; |
if(ff.a==bignum_prec-1&&!open) |
goto done; |
} |
for(k= j;bignum_compare(&gg,&tt)>=open;k++)bignum_dec(&gg,&tt,0x10000000); |
*p++= (j+1+k)>>1; |
done:; |
} |
|
/*:65*/ |
#line 1224 "./mmix-arith.w" |
|
else{ |
if(ff.a> origin)ff.dat[origin]= 0; |
for(e= 1,p= s;gg.a> origin||ff.dat[origin]==gg.dat[origin];){ |
if(gg.a> origin)e--; |
else*p++= ff.dat[origin]+'0',ff.dat[origin]= 0,gg.dat[origin]= 0; |
bignum_times_ten(&ff); |
bignum_times_ten(&gg); |
} |
*p++= ((ff.dat[origin]+1+gg.dat[origin])>>1)+'0'; |
} |
*p= '\0'; |
|
/*:64*/ |
#line 985 "./mmix-arith.w" |
; |
/*67:*/ |
#line 1296 "./mmix-arith.w" |
|
if(e> 17||e<(int)strlen(s)-17) |
printf("%c%s%se%d",s[0],(s[1]?".":""),s+1,e-1); |
else if(e<0)printf(".%0*d%s",-e,0,s); |
else if(strlen(s)>=e)printf("%.*s.%s",e,s,s+e); |
else printf("%s%0*d.",s,e-(int)strlen(s),0); |
|
/*:67*/ |
#line 986 "./mmix-arith.w" |
; |
} |
|
/*:54*//*60:*/ |
#line 1120 "./mmix-arith.w" |
|
static void bignum_times_ten(f) |
bignum*f; |
{ |
register tetra*p,*q;register tetra x,carry; |
for(p= &f->dat[f->b],q= &f->dat[f->a],carry= 0;p>=q;p--){ |
x= *p*10+carry; |
*p= x&0xfffffff; |
carry= x>>28; |
} |
*p= carry; |
if(carry)f->a--; |
if(f->dat[f->b]==0&&f->b> f->a)f->b--; |
} |
|
/*:60*//*61:*/ |
#line 1138 "./mmix-arith.w" |
|
static int bignum_compare(f,g) |
bignum*f,*g; |
{ |
register tetra*p,*pp,*q,*qq; |
if(f->a!=g->a)return f->a> g->a?-1:1; |
pp= &f->dat[f->b],qq= &g->dat[g->b]; |
for(p= &f->dat[f->a],q= &g->dat[g->a];p<=pp;p++,q++){ |
if(*p!=*q)return*p<*q?-1:1; |
if(q==qq)return p<pp; |
} |
return-1; |
} |
|
/*:61*//*62:*/ |
#line 1155 "./mmix-arith.w" |
|
static void bignum_dec(f,g,r) |
bignum*f,*g; |
tetra r; |
{ |
register tetra*p,*q,*qq; |
register int x,borrow; |
while(g->b> f->b)f->dat[++f->b]= 0; |
qq= &g->dat[g->a]; |
for(p= &f->dat[g->b],q= &g->dat[g->b],borrow= 0;q>=qq;p--,q--){ |
x= *p-*q-borrow; |
if(x>=0)borrow= 0,*p= x; |
else borrow= 1,*p= x+r; |
} |
for(;borrow;p--) |
if(*p)borrow= 0,*p= *p-1; |
else*p= r-1; |
while(f->dat[f->a]==0){ |
if(f->a==f->b){ |
f->a= f->b= bignum_prec-1,f->dat[bignum_prec-1]= 0; |
return; |
} |
f->a++; |
} |
while(f->dat[f->b]==0)f->b--; |
} |
|
/*:62*//*68:*/ |
#line 1340 "./mmix-arith.w" |
|
static void bignum_double ARGS((bignum*)); |
int scan_const ARGS((char*)); |
int scan_const(s) |
char*s; |
{ |
/*70:*/ |
#line 1363 "./mmix-arith.w" |
|
register char*p,*q; |
register bool NaN; |
int sign; |
|
/*:70*//*76:*/ |
#line 1435 "./mmix-arith.w" |
|
register char*dec_pt; |
register int exp; |
register int zeros; |
|
/*:76*//*81:*/ |
#line 1503 "./mmix-arith.w" |
|
register int k,x; |
register char*pp; |
bignum ff,tt; |
|
/*:81*/ |
#line 1346 "./mmix-arith.w" |
; |
val.h= val.l= 0; |
p= s; |
if(*p=='+'||*p=='-')sign= *p++;else sign= '+'; |
if(strncmp(p,"NaN",3)==0)NaN= true,p+= 3; |
else NaN= false; |
if((isdigit(*p)&&!NaN)||(*p=='.'&&isdigit(*(p+1)))) |
/*73:*/ |
#line 1396 "./mmix-arith.w" |
|
{ |
for(q= buf0,dec_pt= (char*)0;isdigit(*p);p++){ |
val= oplus(val,shift_left(val,2)); |
val= incr(shift_left(val,1),*p-'0'); |
if(q> buf0||*p!='0') |
if(q<buf_max)*q++= *p; |
else if(*(q-1)=='0')*(q-1)= *p; |
} |
if(NaN)*q++= '1'; |
if(*p=='.')/*74:*/ |
#line 1415 "./mmix-arith.w" |
|
{ |
dec_pt= q; |
p++; |
for(zeros= 0;isdigit(*p);p++) |
if(*p=='0'&&q==buf0)zeros++; |
else if(q<buf_max)*q++= *p; |
else if(*(q-1)=='0')*(q-1)= *p; |
} |
|
/*:74*/ |
#line 1406 "./mmix-arith.w" |
; |
next_char= p; |
if(*p=='e'&&!NaN)/*77:*/ |
#line 1447 "./mmix-arith.w" |
|
{register char exp_sign; |
p++; |
if(*p=='+'||*p=='-')exp_sign= *p++;else exp_sign= '+'; |
if(isdigit(*p)){ |
for(exp= *p++-'0';isdigit(*p);p++) |
if(exp<1000)exp= 10*exp+*p-'0'; |
if(!dec_pt)dec_pt= q,zeros= 0; |
if(exp_sign=='-')exp= -exp; |
next_char= p; |
} |
} |
|
/*:77*/ |
#line 1408 "./mmix-arith.w" |
|
else exp= 0; |
if(dec_pt)/*78:*/ |
#line 1460 "./mmix-arith.w" |
|
{ |
/*79:*/ |
#line 1477 "./mmix-arith.w" |
|
x= buf+341+zeros-dec_pt-exp; |
if(q==buf0||x>=1413){ |
make_it_zero:exp= -99999;goto packit; |
} |
if(x<0){ |
make_it_infinite:exp= 99999;goto packit; |
} |
ff.a= x/9; |
for(p= q;p<q+8;p++)*p= '0'; |
q= q-1-(q+341+zeros-dec_pt-exp)%9; |
for(p= buf0-x%9,k= ff.a;p<=q&&k<=156;p+= 9,k++) |
/*80:*/ |
#line 1497 "./mmix-arith.w" |
|
{ |
for(x= *p-'0',pp= p+1;pp<p+9;pp++)x= 10*x+*pp-'0'; |
ff.dat[k]= x; |
} |
|
/*:80*/ |
#line 1490 "./mmix-arith.w" |
; |
ff.b= k-1; |
for(x= 0;p<=q;p+= 9)if(strncmp(p,"000000000",9)!=0)x= 1; |
ff.dat[156]+= x; |
|
while(ff.dat[ff.b]==0)ff.b--; |
|
/*:79*/ |
#line 1462 "./mmix-arith.w" |
; |
/*83:*/ |
#line 1526 "./mmix-arith.w" |
|
val= zero_octa; |
if(ff.a> 36){ |
for(exp= 0x3fe;ff.a> 36;exp--)bignum_double(&ff); |
for(k= 54;k;k--){ |
if(ff.dat[36]){ |
if(k>=32)val.h|= 1<<(k-32);else val.l|= 1<<k; |
ff.dat[36]= 0; |
if(ff.b==36)break; |
} |
bignum_double(&ff); |
} |
}else{ |
tt.a= tt.b= 36,tt.dat[36]= 2; |
for(exp= 0x3fe;bignum_compare(&ff,&tt)>=0;exp++)bignum_double(&tt); |
for(k= 54;k;k--){ |
bignum_double(&ff); |
if(bignum_compare(&ff,&tt)>=0){ |
if(k>=32)val.h|= 1<<(k-32);else val.l|= 1<<k; |
bignum_dec(&ff,&tt,1000000000); |
if(ff.a==bignum_prec-1)break; |
} |
} |
} |
if(k==0)val.l|= 1; |
|
/*:83*/ |
#line 1463 "./mmix-arith.w" |
; |
packit:/*84:*/ |
#line 1559 "./mmix-arith.w" |
|
val= fpack(val,exp,sign,ROUND_NEAR); |
if(NaN){ |
if((val.h&0x7fffffff)==0x40000000)val.h|= 0x7fffffff,val.l= 0xffffffff; |
else if((val.h&0x7fffffff)==0x3ff00000&&!val.l)val.h|= 0x40000000,val.l= 1; |
else val.h|= 0x40000000; |
} |
|
/*:84*/ |
#line 1464 "./mmix-arith.w" |
; |
return 1; |
} |
|
/*:78*/ |
#line 1410 "./mmix-arith.w" |
; |
if(sign=='-')val= ominus(zero_octa,val); |
return 0; |
} |
|
/*:73*/ |
#line 1353 "./mmix-arith.w" |
; |
if(NaN)/*71:*/ |
#line 1368 "./mmix-arith.w" |
|
{ |
next_char= p; |
val.h= 0x600000,exp= 0x3fe; |
goto packit; |
} |
|
/*:71*/ |
#line 1354 "./mmix-arith.w" |
; |
if(strncmp(p,"Inf",3)==0)/*72:*/ |
#line 1375 "./mmix-arith.w" |
|
{ |
next_char= p+3; |
goto make_it_infinite; |
} |
|
/*:72*/ |
#line 1355 "./mmix-arith.w" |
; |
no_const_found:next_char= s;return-1; |
} |
|
/*:68*//*82:*/ |
#line 1511 "./mmix-arith.w" |
|
static void bignum_double(f) |
bignum*f; |
{ |
register tetra*p,*q;register int x,carry; |
for(p= &f->dat[f->b],q= &f->dat[f->a],carry= 0;p>=q;p--){ |
x= *p+*p+carry; |
if(x>=1000000000)carry= 1,*p= x-1000000000; |
else carry= 0,*p= x; |
} |
*p= carry; |
if(carry)f->a--; |
if(f->dat[f->b]==0&&f->b> f->a)f->b--; |
} |
|
/*:82*//*85:*/ |
#line 1575 "./mmix-arith.w" |
|
int fcomp ARGS((octa,octa)); |
int fcomp(y,z) |
octa y,z; |
{ |
ftype yt,zt; |
int ye,ze; |
char ys,zs; |
octa yf,zf; |
register int x; |
yt= funpack(y,&yf,&ye,&ys); |
zt= funpack(z,&zf,&ze,&zs); |
switch(4*yt+zt){ |
case 4*nan+nan:case 4*zro+nan:case 4*num+nan:case 4*inf+nan: |
case 4*nan+zro:case 4*nan+num:case 4*nan+inf:return 2; |
case 4*zro+zro:return 0; |
case 4*zro+num:case 4*num+zro:case 4*zro+inf:case 4*inf+zro: |
case 4*num+num:case 4*num+inf:case 4*inf+num:case 4*inf+inf: |
if(ys!=zs)x= 1; |
else if(y.h> z.h)x= 1; |
else if(y.h<z.h)x= -1; |
else if(y.l> z.l)x= 1; |
else if(y.l<z.l)x= -1; |
else return 0; |
break; |
} |
return(ys=='-'?-x:x); |
} |
|
/*:85*//*86:*/ |
#line 1608 "./mmix-arith.w" |
|
octa fintegerize ARGS((octa,int)); |
octa fintegerize(z,r) |
octa z; |
int r; |
{ |
ftype zt; |
int ze; |
char zs; |
octa xf,zf; |
zt= funpack(z,&zf,&ze,&zs); |
if(!r)r= cur_round; |
switch(zt){ |
case nan:if(!(z.h&0x80000)){exceptions|= I_BIT;z.h|= 0x80000;} |
case inf:case zro:return z; |
case num:/*87:*/ |
#line 1627 "./mmix-arith.w" |
|
if(ze>=1074)return fpack(zf,ze,zs,ROUND_OFF); |
if(ze<=1020)xf.h= 0,xf.l= 1; |
else{octa oo; |
xf= shift_right(zf,1074-ze,1); |
oo= shift_left(xf,1074-ze); |
if(oo.l!=zf.l||oo.h!=zf.h)xf.l|= 1; |
|
} |
switch(r){ |
case ROUND_DOWN:if(zs=='-')xf= incr(xf,3);break; |
case ROUND_UP:if(zs!='-')xf= incr(xf,3); |
case ROUND_OFF:break; |
case ROUND_NEAR:xf= incr(xf,xf.l&4?2:1);break; |
} |
xf.l&= 0xfffffffc; |
if(ze>=1022)return fpack(shift_left(xf,1074-ze),ze,zs,ROUND_OFF); |
if(xf.l)xf.h= 0x3ff00000,xf.l= 0; |
if(zs=='-')xf.h|= sign_bit; |
return xf; |
|
/*:87*/ |
#line 1623 "./mmix-arith.w" |
; |
} |
} |
|
/*:86*//*88:*/ |
#line 1650 "./mmix-arith.w" |
|
octa fixit ARGS((octa,int)); |
octa fixit(z,r) |
octa z; |
int r; |
{ |
ftype zt; |
int ze; |
char zs; |
octa zf,o; |
zt= funpack(z,&zf,&ze,&zs); |
if(!r)r= cur_round; |
switch(zt){ |
case nan:case inf:exceptions|= I_BIT;return z; |
case zro:return zero_octa; |
case num:if(funpack(fintegerize(z,r),&zf,&ze,&zs)==zro)return zero_octa; |
if(ze<=1076)o= shift_right(zf,1076-ze,1); |
else{ |
if(ze> 1085||(ze==1085&&(zf.h> 0x400000|| |
(zf.h==0x400000&&(zf.l||zs!='-')))))exceptions|= W_BIT; |
if(ze>=1140)return zero_octa; |
o= shift_left(zf,ze-1076); |
} |
return(zs=='-'?ominus(zero_octa,o):o); |
} |
} |
|
/*:88*//*89:*/ |
#line 1681 "./mmix-arith.w" |
|
octa floatit ARGS((octa,int,int,int)); |
octa floatit(z,r,u,p) |
octa z; |
int r; |
int u; |
int p; |
{ |
int e;char s; |
register int t; |
exceptions= 0; |
if(!z.h&&!z.l)return zero_octa; |
if(!r)r= cur_round; |
if(!u&&(z.h&sign_bit))s= '-',z= ominus(zero_octa,z);else s= '+'; |
e= 1076; |
while(z.h<0x400000)e--,z= shift_left(z,1); |
while(z.h>=0x800000){ |
e++; |
t= z.l&1; |
z= shift_right(z,1,1); |
z.l|= t; |
} |
if(p)/*90:*/ |
#line 1707 "./mmix-arith.w" |
|
{ |
register int ex;register tetra t; |
t= sfpack(z,e,s,r); |
ex= exceptions; |
sfunpack(t,&z,&e,&s); |
exceptions= ex; |
} |
|
/*:90*/ |
#line 1703 "./mmix-arith.w" |
; |
return fpack(z,e,s,r); |
} |
|
/*:89*//*91:*/ |
#line 1718 "./mmix-arith.w" |
|
octa froot ARGS((octa,int)); |
octa froot(z,r) |
octa z; |
int r; |
{ |
ftype zt; |
int ze; |
char zs; |
octa x,xf,rf,zf; |
register int xe,k; |
if(!r)r= cur_round; |
zt= funpack(z,&zf,&ze,&zs); |
if(zs=='-'&&zt!=zro)exceptions|= I_BIT,x= standard_NaN; |
else switch(zt){ |
case nan:if(!(z.h&0x80000))exceptions|= I_BIT,z.h|= 0x80000; |
return z; |
case inf:case zro:x= z;break; |
case num:/*92:*/ |
#line 1750 "./mmix-arith.w" |
|
xf.h= 0,xf.l= 2; |
xe= (ze+0x3fe)>>1; |
if(ze&1)zf= shift_left(zf,1); |
rf.h= 0,rf.l= (zf.h>>22)-1; |
for(k= 53;k;k--){ |
rf= shift_left(rf,2);xf= shift_left(xf,1); |
if(k>=43)rf= incr(rf,(zf.h>>(2*(k-43)))&3); |
else if(k>=27)rf= incr(rf,(zf.l>>(2*(k-27)))&3); |
if((rf.l> xf.l&&rf.h>=xf.h)||rf.h> xf.h){ |
xf.l++;rf= ominus(rf,xf);xf.l++; |
} |
} |
if(rf.h||rf.l)xf.l++; |
return fpack(xf,xe,'+',r); |
|
/*:92*/ |
#line 1736 "./mmix-arith.w" |
; |
} |
if(zs=='-')x.h|= sign_bit; |
return x; |
} |
|
/*:91*//*93:*/ |
#line 1774 "./mmix-arith.w" |
|
octa fremstep ARGS((octa,octa,int)); |
octa fremstep(y,z,delta) |
octa y,z; |
int delta; |
{ |
ftype yt,zt; |
int ye,ze; |
char xs,ys,zs; |
octa x,xf,yf,zf; |
register int xe,thresh,odd; |
yt= funpack(y,&yf,&ye,&ys); |
zt= funpack(z,&zf,&ze,&zs); |
switch(4*yt+zt){ |
/*42:*/ |
#line 731 "./mmix-arith.w" |
|
case 4*nan+nan:if(!(y.h&0x80000))exceptions|= I_BIT; |
case 4*zro+nan:case 4*num+nan:case 4*inf+nan: |
if(!(z.h&0x80000))exceptions|= I_BIT,z.h|= 0x80000; |
return z; |
case 4*nan+zro:case 4*nan+num:case 4*nan+inf: |
if(!(y.h&0x80000))exceptions|= I_BIT,y.h|= 0x80000; |
return y; |
|
/*:42*/ |
#line 1788 "./mmix-arith.w" |
; |
case 4*zro+zro:case 4*num+zro:case 4*inf+zro: |
case 4*inf+num:case 4*inf+inf:x= standard_NaN; |
exceptions|= I_BIT;break; |
case 4*zro+num:case 4*zro+inf:case 4*num+inf:return y; |
case 4*num+num:/*94:*/ |
#line 1809 "./mmix-arith.w" |
|
odd= 0; |
thresh= ye-delta; |
if(thresh<ze)thresh= ze; |
while(ye>=thresh)/*95:*/ |
#line 1830 "./mmix-arith.w" |
|
{ |
if(yf.h==zf.h&&yf.l==zf.l)goto zero_out; |
if(yf.h<zf.h||(yf.h==zf.h&&yf.l<zf.l)){ |
if(ye==ze)goto try_complement; |
ye--,yf= shift_left(yf,1); |
} |
yf= ominus(yf,zf); |
if(ye==ze)odd= 1; |
while(yf.h<0x400000)ye--,yf= shift_left(yf,1); |
} |
|
/*:95*/ |
#line 1815 "./mmix-arith.w" |
; |
if(ye>=ze){ |
exceptions|= E_BIT;return fpack(yf,ye,ys,ROUND_OFF); |
} |
if(ye<ze-1)return fpack(yf,ye,ys,ROUND_OFF); |
yf= shift_right(yf,1,1); |
try_complement:xf= ominus(zf,yf),xe= ze,xs= '+'+'-'-ys; |
if(xf.h> yf.h||(xf.h==yf.h&&(xf.l> yf.l||(xf.l==yf.l&&!odd)))) |
xf= yf,xs= ys; |
while(xf.h<0x400000)xe--,xf= shift_left(xf,1); |
return fpack(xf,xe,xs,ROUND_OFF); |
|
/*:94*/ |
#line 1793 "./mmix-arith.w" |
; |
zero_out:x= zero_octa; |
} |
if(ys=='-')x.h|= sign_bit; |
return x; |
} |
|
/*:93*/ |
#line 41 "./mmix-arith.w" |
|
|
/*:1*/ |
/mmix-arith.scn
0,0 → 1,109
\I\X47:Add nonzero numbers and \PB{\&{return}}\X |
\U46. |
\I\X49:Adjust for difference in exponents\X |
\U47. |
\I\X14:Check that \PB{$\|x<\|z$}; otherwise give trivial answer\X |
\U13. |
\I\X51:Compare two numbers with respect to epsilon and \PB{\&{return}}\X |
\U50. |
\I\X53:Compute the difference of fraction parts, \PB{\|o}\X |
\U51. |
\I\X65:Compute the significant digits in the large-exponent case\X |
\U64. |
\I\X64:Compute the significant digits \PB{\|s} and decimal exponent \PB{\|e}\X |
\U54. |
\I\X90:Convert to short float\X |
\U89. |
\I\X83:Determine the binary fraction and binary exponent\X |
\U78. |
\I\X16:Determine the number of significant places \PB{\|n} in the divisor \PB{% |
\|v}\X |
\U13. |
\I\X20:Determine the quotient digit \PB{\|q[\|j]}\X |
\U13. |
\I\X45:Divide nonzero numbers and \PB{\&{return}}\X |
\U44. |
\I\X48:Exchange \PB{\|y} with \PB{\|z}\X |
\Us47\ET51. |
\I\X55:Extract the exponent \PB{\|e} and determine the fraction interval $[f% |
\dts g]$ or $(f\dts g)$\X |
\U54. |
\I\X21:Find the trial quotient, $\hat q$\X |
\U20. |
\I\X4, 9, 30, 32, 69, 75:Global variables\X |
\U1. |
\I\X57:Handle the special case when the fraction part is zero\X |
\U55. |
\I\X23:If the result was negative, decrease $\hat q$ by 1\X |
\U20. |
\I\X87:Integerize and \PB{\&{return}}\X |
\U86. |
\I\X56, 66:Local variables for \PB{\\{print\_float}}\X |
\U54. |
\I\X70, 76, 81:Local variables for \PB{\\{scan\_const}}\X |
\U68. |
\I\X79:Move the digits from \PB{\\{buf}} to \PB{$\ff$}\X |
\U78. |
\I\X43:Multiply nonzero numbers and \PB{\&{return}}\X |
\U41. |
\I\X17:Normalize the divisor\X |
\U13. |
\I\X36, 59:Other type definitions\X |
\U1. |
\I\X84:Pack and round the answer\X |
\U78. |
\I\X19:Pack \PB{\|q} and \PB{\|u} to \PB{\\{acc}} and \PB{\\{aux}}\X |
\U13. |
\I\X11:Pack \PB{\|w} into the outputs \PB{\\{aux}} and \PB{\\{acc}}\X |
\U8. |
\I\X67:Print the significant digits with proper context\X |
\U54. |
\I\X80:Put the 9-digit number \PB{${*}\|p$}\thinspace\dots\thinspace\PB{${*}(% |
\|p+\T{8})$} into \PB{$\ff.\\{dat}[\|k]$}\X |
\U79. |
\I\X95:Reduce \PB{$(\\{ye},\\{yf})$} by a multiple of \PB{\\{zf}}; \PB{\&{goto} |
\\{zero\_out}} if the remainder is zero, \PB{\&{goto} \\{try\_complement}} if |
appropriate\X |
\U94. |
\I\X94:Remainderize nonzero numbers and \PB{\&{return}}\X |
\U93. |
\I\X78:Return a floating point constant\X |
\U73. |
\I\X72:Return infinity\X |
\U68. |
\I\X71:Return the standard NaN\X |
\U68. |
\I\X33:Round and return the result\X |
\U31. |
\I\X35:Round and return the short result\X |
\U34. |
\I\X74:Scan a fraction part\X |
\U73. |
\I\X73:Scan a number and \PB{\&{return}}\X |
\U68. |
\I\X77:Scan an exponent\X |
\U73. |
\I\X63:Store $f$ and $g$ as multiprecise integers\X |
\U54. |
\I\X2:Stuff for \CEE/ preprocessor\X |
\U1. |
\I\X5, 6, 7, 8, 12, 13, 24, 25, 26, 27, 28, 29, 31, 34, 37, 38, 39, 40, 41, 44, |
46, 50, 54, 60, 61, 62, 68, 82, 85, 86, 88, 89, 91, 93:Subroutines\X |
\U1. |
\I\X22:Subtract $b^j\hat q v$ from \PB{\|u}\X |
\U20. |
\I\X92:Take the square root and \PB{\&{return}}\X |
\U91. |
\I\X3:Tetrabyte and octabyte type definitions\X |
\U1. |
\I\X42:The usual NaN cases\X |
\Us41, 44, 46\ETs93. |
\I\X18:Unnormalize the remainder\X |
\U13. |
\I\X15:Unpack the dividend and divisor to \PB{\|u} and \PB{\|v}\X |
\U13. |
\I\X10:Unpack the multiplier and multiplicand to \PB{\|u} and \PB{\|v}\X |
\U8. |
\I\X52:Unsubnormalize \PB{\|y} and \PB{\|z}, if they are subnormal\X |
\U51. |
|
/mmix-arith.idx
0,0 → 1,212
\I\.{\_\_STDC\_\_}, 2. |
\I\|{a}, \[28], \[29], \[59]. |
\I\\{acc}, \[8], 11, \[12], \[13], 19. |
\I\.{ARGS}, \[2], \[5], \[6], \[7], \[8], \[12], \[13], \[24], \[25], \[26], % |
\[27], \[28], \[29], \[31], \[34], \[37], \[38], \[39], \[40], \[41], \[44], % |
\[46], \[50], \[54], \[68], \[85], \[86], \[88], \[89], \[91], \[93]. |
\I\\{aux}, \[4], 8, \[9], 11, 12, 13, 14, 19, 24, 43, 45. |
\I\|{b}, \[28], \[29], \[59]. |
\I\&{bignum}, 54, \[59], 60, 61, 62, 66, 68, 81, 82. |
\I\\{bignum\_compare}, \[54], \[61], 64, 65, 83. |
\I\\{bignum\_dec}, \[54], \[62], 65, 83. |
\I\\{bignum\_double}, \[68], \[82], 83. |
\I\\{bignum\_prec}, \[59], 62, 65, 83. |
\I\\{bignum\_times\_ten}, \[54], \[60], 64, 65, 82. |
\I{binary-to-decimal conversion}, 54. |
\I\&{bool}, \[1], 4, 9, 29, 70. |
\I\\{bool\_mult}, \[29]. |
\I\\{borrow}, \[62]. |
\I\\{buf}, \[75], 76, 79. |
\I\\{buf\_max}, 73, 74, \[75]. |
\I\\{buf0}, 73, 74, \[75], 79. |
\I\\{byte\_diff}, \[27], 28. |
\I\|{c}, \[29]. |
\I\\{carry}, \[60], \[82]. |
\I\\{count\_bits}, \[26], 28. |
\I\\{cur\_round}, \[30], 40, 43, 45, 46, 47, 86, 88, 89, 91. |
\I\|{d}, \[13], \[27], \[46], \[50]. |
\I\.{D\_BIT}, \[31]. |
\I\\{dat}, \[59], 60, 61, 62, 63, 64, 65, 79, 80, 82, 83. |
\I\\{dec\_pt}, 73, 74, \[76], 77, 79. |
\I{decimal-to-binary conversion}, 68. |
\I\\{delta}, \[6], \[93], 94. |
\I\\{done}, \[65]. |
\I\|{e}, \[31], \[34], \[37], \[38], \[39], \[40], \[50], \[56], \[89]. |
\I\.{E\_BIT}, \[31], 93, 94. |
\I\\{ee}, \[37], \[38], \[50], 51, 53. |
\I\\{ef}, \[50], 51, 53. |
\I\\{es}, \[50]. |
\I\\{et}, \[50]. |
\I\\{ex}, \[90]. |
\I\\{exceptions}, 31, \[32], 33, 35, 36, 37, 38, 40, 41, 42, 44, 46, 68, 86, |
88, 89, 90, 91, 93, 94. |
\I\\{exp}, 71, 73, \[76], 77, 79, 83, 84. |
\I\\{exp\_sign}, \[77]. |
\I\|{f}, \[31], \[34], \[37], \[38], \[39], \[40], \[56], \[60], \[61], \[62], % |
\[82]. |
\I\\{false}, 1, 24, 68. |
\I\\{fcomp}, \[85]. |
\I\\{fdivide}, \[44]. |
\I\\{fepscomp}, \[50]. |
\I$\ff$, 63, 64, 65, \[66], 79, 80, \[81], 83. |
\I\\{fintegerize}, \[86], 88. |
\I\\{fixit}, \[88]. |
\I\\{floatit}, \[89]. |
\I\\{fmult}, \[41]. |
\I\\{fpack}, \[31], 34, 36, 39, 43, 45, 46, 47, 49, 84, 87, 89, 92, 94. |
\I\\{fplus}, \[46]. |
\I\\{fremstep}, \[93]. |
\I\\{froot}, \[91]. |
\I\&{ftype}, \[36], 37, 38, 39, 40, 41, 44, 46, 85, 86, 88, 91, 93. |
\I\\{funpack}, 36, \[37], 40, 41, 44, 46, 50, 85, 86, 88, 91, 93. |
\I\|{g}, \[56], \[61], \[62]. |
\I\\{gg}, 63, 64, 65, \[66]. |
\I{Gill, Stanley}, 26. |
\I{Gillies, Donald Bruce}, 26. |
\I\|{h}, \[3]. |
\I\|{i}, \[8], \[13]. |
\I\.{I\_BIT}, \[31], 40, 41, 42, 44, 46, 86, 88, 91, 93. |
\I\\{incr}, \[6], 33, 53, 55, 73, 87, 92. |
\I\\{inf}, \[36], 37, 38, 39, 40, 41, 42, 44, 46, 50, 85, 86, 88, 91, 93. |
\I\\{inf\_octa}, \[4], 39, 41, 44, 46. |
\I\\{isdigit}, 68, 73, 74, 77. |
\I\|{j}, \[8], \[13], \[56]. |
\I\|{k}, \[8], \[13], \[29], \[56], \[81], \[91]. |
\I{Knuth, Donald Ervin}, 58. |
\I\|{l}, \[3]. |
\I\\{list}, 2. |
\I\\{load\_sf}, \[39]. |
\I\|{m}, \[27]. |
\I\\{magic\_offset}, \[63]. |
\I\\{make\_it\_infinite}, 72, \[79]. |
\I\\{make\_it\_zero}, \[79]. |
\I\\{mask}, \[13], 18. |
\I{Miller, Jeffrey Charles Percy}, 26. |
\I{multiprecision conversion}, 54, 68. |
\I{multiprecision division}, 13. |
\I{multiprecision multiplication}, 8. |
\I\|{n}, \[13]. |
\I\\{nan}, \[36], 37, 38, 39, 40, 42, 50, 85, 86, 88, 91. |
\I\\{NaN}, 68, \[70], 73, 84. |
\I\\{neg\_one}, \[4], 24. |
\I\\{negate\_q}, \[24]. |
\I\\{next\_char}, 68, \[69], 71, 72, 73, 77. |
\I\\{no\_const\_found}, \[68]. |
\I\\{num}, \[36], 37, 38, 39, 40, 41, 42, 44, 46, 50, 85, 86, 88, 91, 93. |
\I\|{o}, \[29], \[31], \[34], \[47], \[50], \[88]. |
\I\.{O\_BIT}, \[31], 33, 35. |
\I\\{oand}, \[25]. |
\I\\{oandn}, \[25]. |
\I\&{octa}, \[3], 4, 5, 6, 7, 8, 9, 12, 13, 24, 25, 29, 31, 34, 37, 38, 39, 40, |
41, 44, 46, 47, 50, 54, 56, 69, 85, 86, 87, 88, 89, 91, 93. |
\I\\{odd}, \[93], 94, 95. |
\I\\{odiv}, \[13], 24, 45. |
\I\\{ominus}, \[5], 12, 24, 47, 53, 73, 88, 89, 92, 94, 95. |
\I\\{omult}, \[8], 12, 43. |
\I\\{oo}, \[31], \[34], \[47], 49, \[50], 53, \[87]. |
\I\\{open}, \[65]. |
\I\\{oplus}, \[5], 47, 53, 73. |
\I\\{origin}, \[63], 64, 65. |
\I\\{overflow}, \[4], \[9], 12, 24. |
\I\\{oxor}, \[25]. |
\I\\{o0}, \[34]. |
\I\|{p}, \[60], \[61], \[62], \[66], \[70], \[82], \[89]. |
\I\\{packit}, 71, \[78], 79. |
\I\\{pp}, \[61], 80, \[81]. |
\I\\{print\_float}, \[54], 59. |
\I\\{printf}, 54, 55, 57, 67. |
\I{prototypes for functions}, 2. |
\I\|{q}, \[13], \[24], \[60], \[61], \[62], \[70], \[82]. |
\I\\{qhat}, \[13], 20, 21, 22, 23. |
\I\\{qq}, \[61], \[62]. |
\I\|{r}, \[31], \[34], \[62], \[86], \[88], \[89], \[91]. |
\I{radix conversion}, 54, 68. |
\I\\{rf}, \[91], 92. |
\I\\{rhat}, \[13], 21. |
\I{Rossmanith, Peter}, 26. |
\I\.{ROUND\_DOWN}, \[30], 33, 35, 46, 87. |
\I\.{ROUND\_NEAR}, \[30], 33, 35, 84, 87. |
\I\.{ROUND\_OFF}, \[30], 33, 35, 39, 46, 87, 94. |
\I\.{ROUND\_UP}, \[30], 33, 35, 87. |
\I\|{s}, \[7], \[31], \[34], \[37], \[38], \[39], \[40], \[50], \[66], \[68], % |
\[89]. |
\I\\{scan\_const}, \[68], 69. |
\I{Schwoon, Stefan}, 26. |
\I\\{sfpack}, \[34], 39, 40, 90. |
\I\\{sfunpack}, \[38], 39, 90. |
\I\\{shift\_left}, \[7], 31, 34, 37, 38, 43, 45, 47, 49, 51, 52, 53, 55, 63, |
73, 87, 88, 89, 92, 94, 95. |
\I\\{shift\_right}, \[7], 29, 31, 33, 39, 45, 47, 49, 51, 53, 63, 87, 88, 89, |
94. |
\I\\{sign}, 68, \[70], 73, 84. |
\I\\{sign\_bit}, \[4], 12, 24, 33, 35, 37, 38, 39, 40, 41, 44, 46, 54, 87, 89, |
91, 93. |
\I\\{signed\_odiv}, \[24]. |
\I\\{signed\_omult}, \[12]. |
\I{Singh, Balbir}, 26. |
\I\\{standard\_NaN}, \[4], 41, 44, 46, 91, 93. |
\I{sticky bit}, 31, 34, 49, 53, 79, 87. |
\I\\{store\_sf}, \[40]. |
\I\\{strlen}, 67. |
\I\\{strncmp}, 68, 79. |
\I\\{sy}, \[24]. |
\I{syntax of floating point constants}, 68. |
\I{system dependencies}, 3. |
\I\\{sz}, \[24]. |
\I\|{t}, \[8], \[13], \[39], \[40], \[89], \[90]. |
\I\&{tetra}, \[3], 7, 8, 13, 26, 27, 28, 29, 34, 38, 39, 40, 54, 59, 60, 61, |
62, 82, 90. |
\I\\{thresh}, \[93], 94. |
\I\\{true}, 1, 24, 68. |
\I\\{try\_complement}, \[94], 95. |
\I\\{tt}, 65, \[66], \[81], 83. |
\I\|{u}, \[7], \[8], \[13], \[89]. |
\I\.{U\_BIT}, \[31], 33, 35. |
\I{underflow}, 31. |
\I\|{v}, \[8], \[13]. |
\I\.{V\_BIT}, \[31]. |
\I\\{val}, 68, \[69], 71, 73, 83, 84. |
\I\\{vh}, \[13], 17, 21. |
\I\\{vmh}, \[13], 17, 21. |
\I\|{w}, \[8]. |
\I\.{W\_BIT}, \[31], 88. |
\I{Wheeler, David John}, 26. |
\I{Wilkes, Maurice Vincent}, 26. |
\I\\{wyde\_diff}, \[28]. |
\I\|{x}, \[5], \[6], \[13], \[25], \[26], \[27], \[29], \[37], \[38], \[39], % |
\[40], \[41], \[44], \[46], \[54], \[60], \[62], \[81], \[82], \[85], \[91], % |
\[93]. |
\I\.{X\_BIT}, \[31], 33, 35. |
\I\\{xe}, \[41], 43, \[44], 45, \[46], 47, 49, \[91], 92, \[93], 94. |
\I\\{xf}, \[41], 43, \[44], 45, \[46], 47, \[86], 87, \[91], 92, \[93], 94. |
\I\\{xor}, \[29]. |
\I\\{xs}, \[41], 43, \[44], 45, \[46], 47, \[93], 94. |
\I\\{xx}, \[26]. |
\I\|{y}, \[5], \[6], \[7], \[8], \[12], \[13], \[24], \[25], \[27], \[28], % |
\[29], \[41], \[44], \[46], \[50], \[85], \[93]. |
\I\\{ye}, \[41], 43, \[44], 45, \[46], 47, 48, \[50], 51, 52, \[85], \[93], 94, |
95. |
\I\\{yf}, \[41], 43, \[44], 45, \[46], 47, 48, 49, \[50], 51, 52, 53, \[85], % |
\[93], 94, 95. |
\I\\{yhl}, \[7]. |
\I\\{ylh}, \[7]. |
\I\\{ys}, \[41], \[44], \[46], 47, 48, 49, \[50], 53, \[85], \[93], 94. |
\I\\{yt}, \[41], \[44], \[46], \[50], 52, \[85], \[93]. |
\I\\{yy}, \[24]. |
\I\|{z}, \[5], \[8], \[12], \[13], \[24], \[25], \[27], \[28], \[29], \[39], % |
\[40], \[41], \[44], \[46], \[50], \[85], \[86], \[88], \[89], \[91], \[93]. |
\I\.{Z\_BIT}, \[31], 44. |
\I\\{ze}, \[41], 43, \[44], 45, \[46], 47, 48, \[50], 51, 52, \[85], \[86], 87, |
\[88], \[91], 92, \[93], 94, 95. |
\I\\{zero\_exponent}, \[36], 37, 38, 51. |
\I\\{zero\_octa}, \[4], 24, 29, 31, 39, 41, 44, 45, 46, 53, 73, 83, 88, 89, 93. |
\I\\{zero\_out}, \[93], 95. |
\I\\{zeros}, 74, \[76], 77, 79. |
\I\\{zf}, \[41], 43, \[44], 45, \[46], 47, 48, 49, \[50], 51, 52, 53, \[85], % |
\[86], 87, \[88], \[91], 92, \[93], 94, 95. |
\I\\{zro}, \[36], 37, 38, 39, 40, 41, 42, 44, 46, 50, 52, 85, 86, 88, 91, 93. |
\I\\{zs}, \[41], \[44], \[46], 47, 48, 49, \[50], 53, \[85], \[86], 87, \[88], % |
\[91], \[93]. |
\I\\{zt}, \[41], \[44], \[46], \[50], 52, \[85], \[86], \[88], \[91], \[93]. |
\I\\{zz}, \[24]. |
|
/mmix-arith.w
0,0 → 1,1843
% This file is part of the MMIXware package (c) Donald E Knuth 1999 |
@i boilerplate.w %<< legal stuff: PLEASE READ IT BEFORE MAKING ANY CHANGES! |
|
\def\title{MMIX-ARITH} |
|
\def\MMIX{\.{MMIX}} |
\def\MMIXAL{\.{MMIXAL}} |
\def\Hex#1{\hbox{$^{\scriptscriptstyle\#}$\tt#1}} % experimental hex constant |
\def\dts{\mathinner{\ldotp\ldotp}} |
\def\<#1>{\hbox{$\langle\,$#1$\,\rangle$}}\let\is=\longrightarrow |
\def\ff{\\{ff\kern-.05em}} |
@s ff TeX |
@s bool normal @q unreserve a C++ keyword @> |
@s xor normal @q unreserve a C++ keyword @> |
|
@* Introduction. The subroutines below are used to simulate 64-bit \MMIX\ |
arithmetic on an old-fashioned 32-bit computer---like the one the author |
had when he wrote \MMIXAL\ and the first \MMIX\ simulators in 1998 and 1999. |
All operations are fabricated from 32-bit arithmetic, including |
a full implementation of the IEEE floating point standard, |
assuming only that the \CEE/ compiler has a 32-bit unsigned integer type. |
|
Some day 64-bit machines will be commonplace and the awkward manipulations of |
the present program will look quite archaic. Interested readers who have such |
computers will be able to convert the code to a pure 64-bit form without |
difficulty, thereby obtaining much faster and simpler routines. Meanwhile, |
however, we can simulate the future and hope for continued progress. |
|
This program module has a simple structure, intended to make it |
suitable for loading with \MMIX\ simulators and assemblers. |
|
@c |
#include <stdio.h> |
#include <string.h> |
#include <ctype.h> |
@<Stuff for \CEE/ preprocessor@>@; |
typedef enum{@+false,true@+} bool; |
@<Tetrabyte and octabyte type definitions@>@; |
@<Other type definitions@>@; |
@<Global variables@>@; |
@<Subroutines@> |
|
@ Subroutines of this program are declared first with a prototype, |
as in {\mc ANSI C}, then with an old-style \CEE/ function definition. |
Here are some preprocessor commands that make this work correctly with both |
new-style and old-style compilers. |
@^prototypes for functions@> |
|
@<Stuff for \CEE/ preprocessor@>= |
#ifdef __STDC__ |
#define ARGS(list) list |
#else |
#define ARGS(list) () |
#endif |
|
@ The definition of type \&{tetra} should be changed, if necessary, so that |
it represents an unsigned 32-bit integer. |
@^system dependencies@> |
|
@<Tetra...@>= |
typedef unsigned int tetra; |
/* for systems conforming to the LP-64 data model */ |
typedef struct { tetra h,l;} octa; /* two tetrabytes make one octabyte */ |
|
@ @d sign_bit ((unsigned)0x80000000) |
|
@<Glob...@>= |
octa zero_octa; /* |zero_octa.h=zero_octa.l=0| */ |
octa neg_one={-1,-1}; /* |neg_one.h=neg_one.l=-1| */ |
octa inf_octa={0x7ff00000,0}; /* floating point $+\infty$ */ |
octa standard_NaN={0x7ff80000,0}; /* floating point NaN(.5) */ |
octa aux; /* auxiliary output of a subroutine */ |
bool overflow; /* set by certain subroutines for signed arithmetic */ |
|
@ It's easy to add and subtract octabytes, if we aren't terribly |
worried about speed. |
|
@<Subr...@>= |
octa oplus @,@,@[ARGS((octa,octa))@];@+@t}\6{@> |
octa oplus(y,z) /* compute $y+z$ */ |
octa y,z; |
{@+ octa x; |
x.h=y.h+z.h;@+ |
x.l=y.l+z.l; |
if (x.l<y.l) x.h++; |
return x; |
} |
@# |
octa ominus @,@,@[ARGS((octa,octa))@];@+@t}\6{@> |
octa ominus(y,z) /* compute $y-z$ */ |
octa y,z; |
{@+ octa x; |
x.h=y.h-z.h;@+ |
x.l=y.l-z.l; |
if (x.l>y.l) x.h--; |
return x; |
} |
|
@ In the following subroutine, |delta| is a signed quantity that is |
assumed to fit in a signed tetrabyte. |
|
@<Subr...@>= |
octa incr @,@,@[ARGS((octa,int))@];@+@t}\6{@> |
octa incr(y,delta) /* compute $y+\delta$ */ |
octa y; |
int delta; |
{@+ octa x; |
x.h=y.h;@+ x.l=y.l+delta; |
if (delta>=0) { |
if (x.l<y.l) x.h++; |
}@+else if (x.l>y.l) x.h--; |
return x; |
} |
|
@ Left and right shifts are only a bit more difficult. |
|
@<Subr...@>= |
octa shift_left @,@,@[ARGS((octa,int))@];@+@t}\6{@> |
octa shift_left(y,s) /* shift left by $s$ bits, where $0\le s\le64$ */ |
octa y; |
int s; |
{ |
while (s>=32) y.h=y.l,y.l=0,s-=32; |
if (s) {@+register tetra yhl=y.h<<s,ylh=y.l>>(32-s); |
y.h=yhl+ylh;@+ y.l<<=s; |
} |
return y; |
} |
@# |
octa shift_right @,@,@[ARGS((octa,int,int))@];@+@t}\6{@> |
octa shift_right(y,s,u) /* shift right, arithmetically if $u=0$ */ |
octa y; |
int s,u; |
{ |
while (s>=32) y.l=y.h, y.h=(u?0: -(y.h>>31)), s-=32; |
if (s) {@+register tetra yhl=y.h<<(32-s),ylh=y.l>>s; |
y.h=(u? 0:(-(y.h>>31))<<(32-s))+(y.h>>s);@+ y.l=yhl+ylh; |
} |
return y; |
} |
|
@* Multiplication. We need to multiply two unsigned 64-bit integers, obtaining |
an unsigned 128-bit product. It is easy to do this on a 32-bit machine |
by using Algorithm 4.3.1M of {\sl Seminumerical Algorithms}, with $b=2^{16}$. |
@^multiprecision multiplication@> |
|
The following subroutine returns the lower half of the product, and |
puts the upper half into a global octabyte called |aux|. |
|
@<Subr...@>= |
octa omult @,@,@[ARGS((octa,octa))@];@+@t}\6{@> |
octa omult(y,z) |
octa y,z; |
{ |
register int i,j,k; |
tetra u[4],v[4],w[8]; |
register tetra t; |
octa acc; |
@<Unpack the multiplier and multiplicand to |u| and |v|@>; |
for (j=0;j<4;j++) w[j]=0; |
for (j=0;j<4;j++) |
if (!v[j]) w[j+4]=0; |
else { |
for (i=k=0;i<4;i++) { |
t=u[i]*v[j]+w[i+j]+k; |
w[i+j]=t&0xffff, k=t>>16; |
} |
w[j+4]=k; |
} |
@<Pack |w| into the outputs |aux| and |acc|@>; |
return acc; |
} |
|
@ @<Glob...@>= |
extern octa aux; /* secondary output of subroutines with multiple outputs */ |
extern bool overflow; |
|
@ @<Unpack the mult...@>= |
u[3]=y.h>>16, u[2]=y.h&0xffff, u[1]= y.l>>16, u[0]=y.l&0xffff; |
v[3]=z.h>>16, v[2]=z.h&0xffff, v[1]= z.l>>16, v[0]=z.l&0xffff; |
|
@ @<Pack |w| into the outputs |aux| and |acc|@>= |
aux.h=(w[7]<<16)+w[6], aux.l=(w[5]<<16)+w[4]; |
acc.h=(w[3]<<16)+w[2], acc.l=(w[1]<<16)+w[0]; |
|
@ Signed multiplication has the same lower half product as unsigned |
multiplication. The signed upper half product is obtained with at most two |
further subtractions, after which the result has overflowed if and only if |
the upper half is unequal to 64 copies of the sign bit in the lower half. |
|
@<Subr...@>= |
octa signed_omult @,@,@[ARGS((octa,octa))@];@+@t}\6{@> |
octa signed_omult(y,z) |
octa y,z; |
{ |
octa acc; |
acc=omult(y,z); |
if (y.h&sign_bit) aux=ominus(aux,z); |
if (z.h&sign_bit) aux=ominus(aux,y); |
overflow=(aux.h!=aux.l || (aux.h^(aux.h>>1)^(acc.h&sign_bit))); |
return acc; |
} |
|
@* Division. Long division of an unsigned 128-bit integer by an unsigned |
64-bit integer is, of course, one of the most challenging routines |
needed for \MMIX\ arithmetic. The following program, based on |
Algorithm 4.3.1D of {\sl Seminumerical Algorithms}, computes |
octabytes $q$ and $r$ such that $(2^{64}x+y)=qz+r$ and $0\le r<z$, |
given octabytes $x$, $y$, and~$z$, assuming that $x<z$. |
(If $x\ge z$, it simply sets $q=x$ and $r=y$.) |
The quotient~$q$ is returned by the subroutine; |
the remainder~$r$ is stored in |aux|. |
@^multiprecision division@> |
|
@<Subr...@>= |
octa odiv @,@,@[ARGS((octa,octa,octa))@];@+@t}\6{@> |
octa odiv(x,y,z) |
octa x,y,z; |
{ |
register int i,j,k,n,d; |
tetra u[8],v[4],q[4],mask,qhat,rhat,vh,vmh; |
register tetra t; |
octa acc; |
@<Check that |x<z|; otherwise give trivial answer@>; |
@<Unpack the dividend and divisor to |u| and |v|@>; |
@<Determine the number of significant places |n| in the divisor |v|@>; |
@<Normalize the divisor@>; |
for (j=3;j>=0;j--) @<Determine the quotient digit |q[j]|@>; |
@<Unnormalize the remainder@>; |
@<Pack |q| and |u| to |acc| and |aux|@>; |
return acc; |
} |
|
@ @<Check that |x<z|; otherwise give trivial answer@>= |
if (x.h>z.h || (x.h==z.h && x.l>=z.l)) { |
aux=y;@+ return x; |
} |
|
@ @<Unpack the div...@>= |
u[7]=x.h>>16, u[6]=x.h&0xffff, u[5]=x.l>>16, u[4]=x.l&0xffff; |
u[3]=y.h>>16, u[2]=y.h&0xffff, u[1]=y.l>>16, u[0]=y.l&0xffff; |
v[3]=z.h>>16, v[2]=z.h&0xffff, v[1]=z.l>>16, v[0]=z.l&0xffff; |
|
@ @<Determine the number of significant places |n| in the divisor |v|@>= |
for (n=4;v[n-1]==0;n--); |
|
@ We shift |u| and |v| left by |d| places, where |d| is chosen to |
make $2^{15}\le v_{n-1}<2^{16}$. |
|
@<Normalize the divisor@>= |
vh=v[n-1]; |
for (d=0;vh<0x8000;d++,vh<<=1); |
for (j=k=0; j<n+4; j++) { |
t=(u[j]<<d)+k; |
u[j]=t&0xffff, k=t>>16; |
} |
for (j=k=0; j<n; j++) { |
t=(v[j]<<d)+k; |
v[j]=t&0xffff, k=t>>16; |
} |
vh=v[n-1]; |
vmh=(n>1? v[n-2]: 0); |
|
@ @<Unnormalize the remainder@>= |
mask=(1<<d)-1; |
for (j=3; j>=n; j--) u[j]=0; |
for (k=0;j>=0;j--) { |
t=(k<<16)+u[j]; |
u[j]=t>>d, k=t&mask; |
} |
|
@ @<Pack |q| and |u| to |acc| and |aux|@>= |
acc.h=(q[3]<<16)+q[2], acc.l=(q[1]<<16)+q[0]; |
aux.h=(u[3]<<16)+u[2], aux.l=(u[1]<<16)+u[0]; |
|
@ @<Determine the quotient digit |q[j]|@>= |
{ |
@<Find the trial quotient, $\hat q$@>; |
@<Subtract $b^j\hat q v$ from |u|@>; |
@<If the result was negative, decrease $\hat q$ by 1@>; |
q[j]=qhat; |
} |
|
@ @<Find the trial quotient, $\hat q$@>= |
t=(u[j+n]<<16)+u[j+n-1]; |
qhat=t/vh, rhat=t-vh*qhat; |
if (n>1) while (qhat==0x10000 || qhat*vmh>(rhat<<16)+u[j+n-2]) { |
qhat--, rhat+=vh; |
if (rhat>=0x10000) break; |
} |
|
@ After this step, |u[j+n]| will either equal |k| or |k-1|. The |
true value of~|u| would be obtained by subtracting~|k| from |u[j+n]|; |
but we don't have to fuss over |u[j+n]|, because it won't be examined later. |
|
@<Subtract $b^j\hat q v$ from |u|@>= |
for (i=k=0; i<n; i++) { |
t=u[i+j]+0xffff0000-k-qhat*v[i]; |
u[i+j]=t&0xffff, k=0xffff-(t>>16); |
} |
|
@ The correction here occurs only rarely, but it can be necessary---for |
example, when dividing the number \Hex{7fff800100000000} by \Hex{800080020005}. |
|
@<If the result was negative, decrease $\hat q$ by 1@>= |
if (u[j+n]!=k) { |
qhat--; |
for (i=k=0; i<n; i++) { |
t=u[i+j]+v[i]+k; |
u[i+j]=t&0xffff, k=t>>16; |
} |
} |
|
@ Signed division can be reduced to unsigned division in a tedious |
but straightforward manner. We assume that the divisor isn't zero. |
|
@<Subr...@>= |
octa signed_odiv @,@,@[ARGS((octa,octa))@];@+@t}\6{@> |
octa signed_odiv(y,z) |
octa y,z; |
{ |
octa yy,zz,q; |
register int sy,sz; |
if (y.h&sign_bit) sy=2, yy=ominus(zero_octa,y); |
else sy=0, yy=y; |
if (z.h&sign_bit) sz=1, zz=ominus(zero_octa,z); |
else sz=0, zz=z; |
q=odiv(zero_octa,yy,zz); |
overflow=false; |
switch (sy+sz) { |
case 2+1: aux=ominus(zero_octa,aux); |
if (q.h==sign_bit) overflow=true; |
case 0+0: return q; |
case 2+0:@+ if (aux.h || aux.l) aux=ominus(zz,aux); |
goto negate_q; |
case 0+1:@+ if (aux.h || aux.l) aux=ominus(aux,zz); |
negate_q:@+ if (aux.h || aux.l) return ominus(neg_one,q); |
else return ominus(zero_octa,q); |
} |
} |
|
@* Bit fiddling. The bitwise operators of \MMIX\ are fairly easy to |
implement directly, but three of them occur often enough to deserve |
packaging as subroutines. |
|
@<Subr...@>= |
octa oand @,@,@[ARGS((octa,octa))@];@+@t}\6{@> |
octa oand(y,z) /* compute $y\land z$ */ |
octa y,z; |
{@+ octa x; |
x.h=y.h&z.h;@+ x.l=y.l&z.l; |
return x; |
} |
@# |
octa oandn @,@,@[ARGS((octa,octa))@];@+@t}\6{@> |
octa oandn(y,z) /* compute $y\land\bar z$ */ |
octa y,z; |
{@+ octa x; |
x.h=y.h&~z.h;@+ x.l=y.l&~z.l; |
return x; |
} |
@# |
octa oxor @,@,@[ARGS((octa,octa))@];@+@t}\6{@> |
octa oxor(y,z) /* compute $y\oplus z$ */ |
octa y,z; |
{@+ octa x; |
x.h=y.h^z.h;@+ x.l=y.l^z.l; |
return x; |
} |
|
@ Here's a fun way to count the number of bits in a tetrabyte. |
[This classical trick is called the ``Gillies--Miller method |
for sideways addition'' in {\sl The Preparation of Programs |
for an Electronic Digital Computer\/} by Wilkes, Wheeler, and |
Gill, second edition (Reading, Mass.:\ Addison--Wesley, 1957), |
191--193. Some of the tricks used here were suggested by |
Balbir Singh, Peter Rossmanith, and Stefan Schwoon.] |
@^Gillies, Donald Bruce@> |
@^Miller, Jeffrey Charles Percy@> |
@^Wilkes, Maurice Vincent@> |
@^Wheeler, David John@> |
@^Gill, Stanley@> |
@^Singh, Balbir@> |
@^Rossmanith, Peter@> |
@^Schwoon, Stefan@> |
|
@<Subr...@>= |
int count_bits @,@,@[ARGS((tetra))@];@+@t}\6{@> |
int count_bits(x) |
tetra x; |
{ |
register int xx=x; |
xx=xx-((xx>>1)&0x55555555); |
xx=(xx&0x33333333)+((xx>>2)&0x33333333); |
xx=(xx+(xx>>4))&0x0f0f0f0f; |
xx=xx+(xx>>8); |
return (xx+(xx>>16)) & 0xff; |
} |
|
@ To compute the nonnegative byte differences of two given tetrabytes, |
we can carry out the following 20-step branchless computation: |
|
@<Subr...@>= |
tetra byte_diff @,@,@[ARGS((tetra,tetra))@];@+@t}\6{@> |
tetra byte_diff(y,z) |
tetra y,z; |
{ |
register tetra d=(y&0x00ff00ff)+0x01000100-(z&0x00ff00ff); |
register tetra m=d&0x01000100; |
register tetra x=d&(m-(m>>8)); |
d=((y>>8)&0x00ff00ff)+0x01000100-((z>>8)&0x00ff00ff); |
m=d&0x01000100; |
return x+((d&(m-(m>>8)))<<8); |
} |
|
@ To compute the nonnegative wyde differences of two tetrabytes, |
another trick leads to a 15-step branchless computation. |
(Research problem: Can |count_bits|, |byte_diff|, or |wyde_diff| be done |
with fewer operations?) |
|
@<Subr...@>= |
tetra wyde_diff @,@,@[ARGS((tetra,tetra))@];@+@t}\6{@> |
tetra wyde_diff(y,z) |
tetra y,z; |
{ |
register tetra a=((y>>16)-(z>>16))&0x10000; |
register tetra b=((y&0xffff)-(z&0xffff))&0x10000; |
return y-(z^((y^z)&(b-a-(b>>16)))); |
} |
|
@ The last bitwise subroutine we need is the most interesting: |
It implements \MMIX's \.{MOR} and \.{MXOR} operations. |
|
@<Subr...@>= |
octa bool_mult @,@,@[ARGS((octa,octa,bool))@];@+@t}\6{@> |
octa bool_mult(y,z,xor) |
octa y,z; /* the operands */ |
bool xor; /* do we do xor instead of or? */ |
{ |
octa o,x; |
register tetra a,b,c; |
register int k; |
for (k=0,o=y,x=zero_octa;o.h||o.l;k++,o=shift_right(o,8,1)) |
if (o.l&0xff) { |
a=((z.h>>k)&0x01010101)*0xff; |
b=((z.l>>k)&0x01010101)*0xff; |
c=(o.l&0xff)*0x01010101; |
if (xor) x.h^=a&c, x.l^=b&c; |
else x.h|=a&c, x.l|=b&c; |
} |
return x; |
} |
|
@* Floating point packing and unpacking. Standard IEEE floating binary |
numbers pack a sign, exponent, and fraction into a tetrabyte |
or octabyte. In this section we consider basic subroutines that |
convert between IEEE format and the separate unpacked components. |
|
@d ROUND_OFF 1 |
@d ROUND_UP 2 |
@d ROUND_DOWN 3 |
@d ROUND_NEAR 4 |
|
@<Glob...@>= |
int cur_round; /* the current rounding mode */ |
|
@ The |fpack| routine takes an octabyte $f$, a raw exponent~$e$, |
and a sign~|s|, and packs them |
into the floating binary number that corresponds to |
$\pm2^{e-1076}f$, using a given rounding mode. |
The value of $f$ should satisfy $2^{54}\le f\le 2^{55}$. |
|
Thus, for example, the floating binary number $+1.0=\Hex{3ff0000000000000}$ |
is obtained when $f=2^{54}$, $e=\Hex{3fe}$, and |s='+'|. |
The raw exponent~$e$ is usually one less than |
the final exponent value; the leading bit of~$f$ is essentially added |
to the exponent. (This trick works nicely for subnormal numbers, when |
$e<0$, or in cases where the value of $f$ is rounded upwards to $2^{55}$.) |
|
Exceptional events are noted by oring appropriate bits into |
the global variable |exceptions|. Special considerations apply to |
underflow, which is not fully specified by Section 7.4 of the IEEE standard: |
Implementations of the standard are free to choose between two definitions |
of ``tininess'' and two definitions of ``accuracy loss.'' |
\MMIX\ determines tininess {\it after\/} rounding, hence a result with |
$e<0$ is not necessarily tiny; \MMIX\ treats accuracy loss as equivalent |
to inexactness. Thus, a result underflows if and only if |
it is tiny and either (i)~it is inexact or (ii)~the underflow trap is enabled. |
The |fpack| routine sets |U_BIT| in |exceptions| if and only if the result is |
tiny, |X_BIT| if and only if the result is inexact. |
@^underflow@> |
|
@d X_BIT (1<<8) /* floating inexact */ |
@d Z_BIT (1<<9) /* floating division by zero */ |
@d U_BIT (1<<10) /* floating underflow */ |
@d O_BIT (1<<11) /* floating overflow */ |
@d I_BIT (1<<12) /* floating invalid operation */ |
@d W_BIT (1<<13) /* float-to-fix overflow */ |
@d V_BIT (1<<14) /* integer overflow */ |
@d D_BIT (1<<15) /* integer divide check */ |
@d E_BIT (1<<18) /* external (dynamic) trap bit */ |
|
@<Subr...@>= |
octa fpack @,@,@[ARGS((octa,int,char,int))@];@+@t}\6{@> |
octa fpack(f,e,s,r) |
octa f; /* the normalized fraction part */ |
int e; /* the raw exponent */ |
char s; /* the sign */ |
int r; /* the rounding mode */ |
{ |
octa o; |
if (e>0x7fd) e=0x7ff, o=zero_octa; |
else { |
if (e<0) { |
if (e<-54) o.h=0, o.l=1; |
else {@+octa oo; |
o=shift_right(f,-e,1); |
oo=shift_left(o,-e); |
if (oo.l!=f.l || oo.h!=f.h) o.l |= 1; /* sticky bit */ |
@^sticky bit@> |
} |
e=0; |
}@+else o=f; |
} |
@<Round and return the result@>; |
} |
|
@ @<Glob...@>= |
int exceptions; /* bits possibly destined for rA */ |
|
@ Everything falls together so nicely here, it's almost too good to be true! |
|
@<Round and return the result@>= |
if (o.l&3) exceptions |= X_BIT; |
switch (r) { |
case ROUND_DOWN:@+ if (s=='-') o=incr(o,3);@+break; |
case ROUND_UP:@+ if (s!='-') o=incr(o,3); |
case ROUND_OFF: break; |
case ROUND_NEAR: o=incr(o, o.l&4? 2: 1);@+break; |
} |
o = shift_right(o,2,1); |
o.h += e<<20; |
if (o.h>=0x7ff00000) exceptions |= O_BIT+X_BIT; /* overflow */ |
else if (o.h<0x100000) exceptions |= U_BIT; /* tininess */ |
if (s=='-') o.h |= sign_bit; |
return o; |
|
@ Similarly, |sfpack| packs a short float, from inputs |
having the same conventions as |fpack|. |
|
@<Subr...@>= |
tetra sfpack @,@,@[ARGS((octa,int,char,int))@];@+@t}\6{@> |
tetra sfpack(f,e,s,r) |
octa f; /* the fraction part */ |
int e; /* the raw exponent */ |
char s; /* the sign */ |
int r; /* the rounding mode */ |
{ |
register tetra o; |
if (e>0x47d) e=0x47f, o=0; |
else { |
o=shift_left(f,3).h; |
if (f.l&0x1fffffff) o|=1; |
if (e<0x380) { |
if (e<0x380-25) o=1; |
else {@+register tetra o0,oo; |
o0 = o; |
o = o>>(0x380-e); |
oo = o<<(0x380-e); |
if (oo!=o0) o |= 1; /* sticky bit */ |
@^sticky bit@> |
} |
e=0x380; |
} |
} |
@<Round and return the short result@>; |
} |
|
@ @<Round and return the short result@>= |
if (o&3) exceptions |= X_BIT; |
switch (r) { |
case ROUND_DOWN:@+ if (s=='-') o+=3;@+break; |
case ROUND_UP:@+ if (s!='-') o+=3; |
case ROUND_OFF: break; |
case ROUND_NEAR: o+=(o&4? 2: 1);@+break; |
} |
o = o>>2; |
o += (e-0x380)<<23; |
if (o>=0x7f800000) exceptions |= O_BIT+X_BIT; /* overflow */ |
else if (o<0x100000) exceptions |= U_BIT; /* tininess */ |
if (s=='-') o |= sign_bit; |
return o; |
|
@ The |funpack| routine is, roughly speaking, the opposite of |fpack|. |
It takes a given floating point number~$x$ and separates out its |
fraction part~$f$, exponent~$e$, and sign~$s$. It clears |exceptions| |
to zero. It returns the type of value found: |zro|, |num|, |inf|, |
or |nan|. When it returns |num|, |
it will have set $f$, $e$, and~$s$ |
to the values from which |fpack| would produce the original number~$x$ |
without exceptions. |
|
@d zero_exponent (-1000) /* zero is assumed to have this exponent */ |
|
@<Other type...@>= |
typedef enum {@!zro,@!num,@!inf,@!nan}@+ftype; |
|
@ @<Subr...@>= |
ftype funpack @,@,@[ARGS((octa,octa*,int*,char*))@];@+@t}\6{@> |
ftype funpack(x,f,e,s) |
octa x; /* the given floating point value */ |
octa *f; /* address where the fraction part should be stored */ |
int *e; /* address where the exponent part should be stored */ |
char *s; /* address where the sign should be stored */ |
{ |
register int ee; |
exceptions=0; |
*s=(x.h&sign_bit? '-': '+'); |
*f=shift_left(x,2); |
f->h &= 0x3fffff; |
ee=(x.h>>20)&0x7ff; |
if (ee) { |
*e=ee-1; |
f->h |= 0x400000; |
return (ee<0x7ff? num: f->h==0x400000 && !f->l? inf: nan); |
} |
if (!x.l && !f->h) { |
*e=zero_exponent;@+ return zro; |
} |
do {@+ ee--;@+ *f=shift_left(*f,1);@+} while (!(f->h&0x400000)); |
*e=ee;@+ return num; |
} |
|
@ @<Subr...@>= |
ftype sfunpack @,@,@[ARGS((tetra,octa*,int*,char*))@];@+@t}\6{@> |
ftype sfunpack(x,f,e,s) |
tetra x; /* the given floating point value */ |
octa *f; /* address where the fraction part should be stored */ |
int *e; /* address where the exponent part should be stored */ |
char *s; /* address where the sign should be stored */ |
{ |
register int ee; |
exceptions=0; |
*s=(x&sign_bit? '-': '+'); |
f->h=(x>>1)&0x3fffff, f->l=x<<31; |
ee=(x>>23)&0xff; |
if (ee) { |
*e=ee+0x380-1; |
f->h |= 0x400000; |
return (ee<0xff? num: (x&0x7fffffff)==0x7f800000? inf: nan); |
} |
if (!(x&0x7fffffff)) { |
*e=zero_exponent;@+return zro; |
} |
do {@+ ee--;@+ *f=shift_left(*f,1);@+} while (!(f->h&0x400000)); |
*e=ee+0x380;@+ return num; |
} |
|
@ Since \MMIX\ downplays 32-bit operations, it uses |sfpack| and |sfunpack| |
only when loading and storing short floats, or when converting |
from fixed point to floating point. |
|
@<Subr...@>= |
octa load_sf @,@,@[ARGS((tetra))@];@+@t}\6{@> |
octa load_sf(z) |
tetra z; /* 32 bits to be loaded into a 64-bit register */ |
{ |
octa f,x;@+int e;@+char s;@+ftype t; |
t=sfunpack(z,&f,&e,&s); |
switch (t) { |
case zro: x=zero_octa;@+break; |
case num: return fpack(f,e,s,ROUND_OFF); |
case inf: x=inf_octa;@+break; |
case nan: x=shift_right(f,2,1);@+x.h|=0x7ff00000;@+break; |
} |
if (s=='-') x.h|=sign_bit; |
return x; |
} |
|
@ @<Subr...@>= |
tetra store_sf @,@,@[ARGS((octa))@];@+@t}\6{@> |
tetra store_sf(x) |
octa x; /* 64 bits to be loaded into a 32-bit word */ |
{ |
octa f;@+tetra z;@+int e;@+char s;@+ftype t; |
t=funpack(x,&f,&e,&s); |
switch (t) { |
case zro: z=0;@+break; |
case num: return sfpack(f,e,s,cur_round); |
case inf: z=0x7f800000;@+break; |
case nan:@+ if (!(f.h&0x200000)) { |
f.h|=0x200000;@+exceptions|=I_BIT; /* NaN was signaling */ |
} |
z=0x7f800000|(f.h<<1)|(f.l>>31);@+break; |
} |
if (s=='-') z|=sign_bit; |
return z; |
} |
|
@* Floating multiplication and division. |
The hardest fixed point operations were multiplication and division; |
but these two operations are the {\it easiest\/} to implement in floating point |
arithmetic, once their fixed point counterparts are available. |
|
@<Subr...@>= |
octa fmult @,@,@[ARGS((octa,octa))@];@+@t}\6{@> |
octa fmult(y,z) |
octa y,z; |
{ |
ftype yt,zt; |
int ye,ze; |
char ys,zs; |
octa x,xf,yf,zf; |
register int xe; |
register char xs; |
yt=funpack(y,&yf,&ye,&ys); |
zt=funpack(z,&zf,&ze,&zs); |
xs=ys+zs-'+'; /* will be |'-'| when the result is negative */ |
switch (4*yt+zt) { |
@t\4@>@<The usual NaN cases@>; |
case 4*zro+zro: case 4*zro+num: case 4*num+zro: x=zero_octa;@+break; |
case 4*num+inf: case 4*inf+num: case 4*inf+inf: x=inf_octa;@+break; |
case 4*zro+inf: case 4*inf+zro: x=standard_NaN; |
exceptions|=I_BIT;@+break; |
case 4*num+num: @<Multiply nonzero numbers and |return|@>; |
} |
if (xs=='-') x.h|=sign_bit; |
return x; |
} |
|
@ @<The usual NaN cases@>= |
case 4*nan+nan:@+if (!(y.h&0x80000)) exceptions|=I_BIT; /* |y| is signaling */ |
case 4*zro+nan: case 4*num+nan: case 4*inf+nan: |
if (!(z.h&0x80000)) exceptions|=I_BIT, z.h|=0x80000; |
return z; |
case 4*nan+zro: case 4*nan+num: case 4*nan+inf: |
if (!(y.h&0x80000)) exceptions|=I_BIT, y.h|=0x80000; |
return y; |
|
@ @<Multiply nonzero numbers and |return|@>= |
xe=ye+ze-0x3fd; /* the raw exponent */ |
x=omult(yf,shift_left(zf,9)); |
if (aux.h>=0x400000) xf=aux; |
else xf=shift_left(aux,1), xe--; |
if (x.h||x.l) xf.l|=1; /* adjust the sticky bit */ |
return fpack(xf,xe,xs,cur_round); |
|
@ @<Subr...@>= |
octa fdivide @,@,@[ARGS((octa,octa))@];@+@t}\6{@> |
octa fdivide(y,z) |
octa y,z; |
{ |
ftype yt,zt; |
int ye,ze; |
char ys,zs; |
octa x,xf,yf,zf; |
register int xe; |
register char xs; |
yt=funpack(y,&yf,&ye,&ys); |
zt=funpack(z,&zf,&ze,&zs); |
xs=ys+zs-'+'; /* will be |'-'| when the result is negative */ |
switch (4*yt+zt) { |
@t\4@>@<The usual NaN cases@>; |
case 4*zro+inf: case 4*zro+num: case 4*num+inf: x=zero_octa;@+break; |
case 4*num+zro: exceptions|=Z_BIT; |
case 4*inf+num: case 4*inf+zro: x=inf_octa;@+break; |
case 4*zro+zro: case 4*inf+inf: x=standard_NaN; |
exceptions|=I_BIT;@+break; |
case 4*num+num: @<Divide nonzero numbers and |return|@>; |
} |
if (xs=='-') x.h|=sign_bit; |
return x; |
} |
|
@ @<Divide nonzero numbers...@>= |
xe=ye-ze+0x3fd; /* the raw exponent */ |
xf=odiv(yf,zero_octa,shift_left(zf,9)); |
if (xf.h>=0x800000) { |
aux.l|=xf.l&1; |
xf=shift_right(xf,1,1); |
xe++; |
} |
if (aux.h||aux.l) xf.l|=1; /* adjust the sticky bit */ |
return fpack(xf,xe,xs,cur_round); |
|
@*Floating addition and subtraction. Now for the bread-and-butter |
operation, the sum of two floating point numbers. |
It is not terribly difficult, but many cases need to be handled carefully. |
|
@<Subr...@>= |
octa fplus @,@,@[ARGS((octa,octa))@];@+@t}\6{@> |
octa fplus(y,z) |
octa y,z; |
{ |
ftype yt,zt; |
int ye,ze; |
char ys,zs; |
octa x,xf,yf,zf; |
register int xe,d; |
register char xs; |
yt=funpack(y,&yf,&ye,&ys); |
zt=funpack(z,&zf,&ze,&zs); |
switch (4*yt+zt) { |
@t\4@>@<The usual NaN cases@>; |
case 4*zro+num: return fpack(zf,ze,zs,ROUND_OFF);@+break; /* may underflow */ |
case 4*num+zro: return fpack(yf,ye,ys,ROUND_OFF);@+break; /* may underflow */ |
case 4*inf+inf:@+if (ys!=zs) { |
exceptions|=I_BIT;@+x=standard_NaN;@+xs=zs;@+break; |
} |
case 4*num+inf: case 4*zro+inf: x=inf_octa;@+xs=zs;@+break; |
case 4*inf+num: case 4*inf+zro: x=inf_octa;@+xs=ys;@+break; |
case 4*num+num:@+ if (y.h!=(z.h^0x80000000) || y.l!=z.l) |
@<Add nonzero numbers and |return|@>; |
case 4*zro+zro: x=zero_octa; |
xs=(ys==zs? ys: cur_round==ROUND_DOWN? '-': '+');@+break; |
} |
if (xs=='-') x.h|=sign_bit; |
return x; |
} |
|
@ @<Add nonzero numbers...@>= |
{@+octa o,oo; |
if (ye<ze || (ye==ze && (yf.h<zf.h || (yf.h==zf.h && yf.l<zf.l)))) |
@<Exchange |y| with |z|@>; |
d=ye-ze; |
xs=ys, xe=ye; |
if (d) @<Adjust for difference in exponents@>; |
if (ys==zs) { |
xf=oplus(yf,zf); |
if (xf.h>=0x800000) xe++, d=xf.l&1, xf=shift_right(xf,1,1), xf.l|=d; |
}@+else { |
xf=ominus(yf,zf); |
if (xf.h>=0x800000) xe++, d=xf.l&1, xf=shift_right(xf,1,1), xf.l|=d; |
else@+ while (xf.h<0x400000) xe--, xf=shift_left(xf,1); |
} |
return fpack(xf,xe,xs,cur_round); |
} |
|
@ @<Exchange |y| with |z|@>= |
{ |
o=yf, yf=zf, zf=o; |
d=ye, ye=ze, ze=d; |
d=ys, ys=zs, zs=d; |
} |
|
@ Proper rounding requires two bits to the right of the fraction delivered |
to~|fpack|. The first is the true next bit of the result; |
the other is a ``sticky'' bit, which is nonzero if any further bits of the |
true result are nonzero. Sticky rounding to an integer takes |
$x$ into the number $\lfloor x/2\rfloor+\lceil x/2\rceil$. |
@^sticky bit@> |
|
Some subtleties need to be observed here, in order to |
prevent the sticky bit from being shifted left. If we did not |
shift |yf| left~1 before shifting |zf| to the right, an incorrect |
answer would be obtained in certain cases---for example, if |
$|yf|=2^{54}$, $|zf|=2^{54}+2^{53}-1$, $d=52$. |
|
@<Adjust for difference in exponents@>= |
{ |
if (d<=2) zf=shift_right(zf,d,1); /* exact result */ |
else if (d>53) zf.h=0, zf.l=1; /* tricky but OK */ |
else { |
if (ys!=zs) d--,xe--,yf=shift_left(yf,1); |
o=zf; |
zf=shift_right(o,d,1); |
oo=shift_left(zf,d); |
if (oo.l!=o.l || oo.h!=o.h) zf.l|=1; |
} |
} |
|
@ The comparison of floating point numbers with respect to $\epsilon$ |
shares some of the characteristics of floating point addition/subtraction. |
In some ways it is simpler, and in other ways it is more difficult; |
we might as well deal with it now. % anyways |
|
Subroutine |fepscomp(y,z,e,s)| returns 2 if |y|, |z|, or |e| is a NaN |
or |e| is negative. It returns 1 if |s=0| and $y\approx z\ (e)$ or if |
|s!=0| and $y\sim z\ (e)$, |
as defined in Section~4.2.2 of {\sl Seminumerical Algorithms\/}; |
otherwise it returns~0. |
|
@<Subr...@>= |
int fepscomp @,@,@[ARGS((octa,octa,octa,int))@];@+@t}\6{@> |
int fepscomp(y,z,e,s) |
octa y,z,e; /* the operands */ |
int s; /* test similarity? */ |
{ |
octa yf,zf,ef,o,oo; |
int ye,ze,ee; |
char ys,zs,es; |
register int yt,zt,et,d; |
et=funpack(e,&ef,&ee,&es); |
if (es=='-') return 2; |
switch (et) { |
case nan: return 2; |
case inf: ee=10000; |
case num: case zro: break; |
} |
yt=funpack(y,&yf,&ye,&ys); |
zt=funpack(z,&zf,&ze,&zs); |
switch (4*yt+zt) { |
case 4*nan+nan: case 4*nan+inf: case 4*nan+num: case 4*nan+zro: |
case 4*inf+nan: case 4*num+nan: case 4*zro+nan: return 2; |
case 4*inf+inf: return (ys==zs || ee>=1023); |
case 4*inf+num: case 4*inf+zro: case 4*num+inf: case 4*zro+inf: |
return (s && ee>=1022); |
case 4*zro+zro: return 1; |
case 4*zro+num: case 4*num+zro:@+ if (!s) return 0; |
case 4*num+num: break; |
} |
@<Compare two numbers with respect to epsilon and |return|@>; |
} |
|
@ The relation $y\approx z\ (\epsilon)$ reduces to |
$y\sim z\ (\epsilon/2^d)$, if $d$~is the difference between the |
larger and smaller exponents of $y$ and~$z$. |
|
@<Compare two numbers with respect to epsilon and |return|@>= |
@<Unsubnormalize |y| and |z|, if they are subnormal@>; |
if (ye<ze || (ye==ze && (yf.h<zf.h || (yf.h==zf.h && yf.l<zf.l)))) |
@<Exchange |y| with |z|@>; |
if (ze==zero_exponent) ze=ye; |
d=ye-ze; |
if (!s) ee-=d; |
if (ee>=1023) return 1; /* if $\epsilon\ge2$, $z\in N_\epsilon(y)$ */ |
@<Compute the difference of fraction parts, |o|@>; |
if (!o.h && !o.l) return 1; |
if (ee<968) return 0; /* if $y\ne z$ and $\epsilon<2^{-54}$, $y\not\sim z$ */ |
if (ee>=1021) ef=shift_left(ef,ee-1021); |
else ef=shift_right(ef,1021-ee,1); |
return o.h<ef.h || (o.h==ef.h && o.l<=ef.l); |
|
@ @<Unsubnormalize |y| and |z|, if they are subnormal@>= |
if (ye<0 && yt!=zro) yf=shift_left(y,2), ye=0; |
if (ze<0 && zt!=zro) zf=shift_left(z,2), ze=0; |
|
@ At this point $y\sim z$ if and only if |
$$|yf|+(-1)^{[ys=zs]}|zf|/2^d\le 2^{ee-1021}|ef|=2^{55}\epsilon.$$ |
We need to evaluate this relation without overstepping the bounds of |
our simulated 64-bit registers. |
|
When $d>2$, the difference of fraction parts might not fit exactly |
in an octabyte; |
in that case the numbers are not similar unless $\epsilon>3/8$, |
and we replace the difference by the ceiling of the |
true result. When $\epsilon<1/8$, our program essentially replaces |
$2^{55}\epsilon$ by $\lfloor2^{55}\epsilon\rfloor$. These |
truncations are not needed simultaneously. Therefore the logic |
is justified by the facts that, if $n$ is an integer, we have |
$x\le n$ if and only if $\lceil x\rceil\le n$; |
$n\le x$ if and only if $n\le\lfloor x\rfloor$. (Notice that the |
concept of ``sticky bit'' is {\it not\/} appropriate here.) |
@^sticky bit@> |
|
@<Compute the difference of fraction parts, |o|@>= |
if (d>54) o=zero_octa,oo=zf; |
else o=shift_right(zf,d,1),oo=shift_left(o,d); |
if (oo.h!=zf.h || oo.l!=zf.l) { /* truncated result, hence $d>2$ */ |
if (ee<1020) return 0; /* difference is too large for similarity */ |
o=incr(o,ys==zs? 0: 1); /* adjust for ceiling */ |
} |
o=(ys==zs? ominus(yf,o): oplus(yf,o)); |
|
@*Floating point output conversion. |
The |print_float| routine converts an octabyte to a floating decimal |
representation that will be input as precisely the same value. |
@^binary-to-decimal conversion@> |
@^radix conversion@> |
@^multiprecision conversion@> |
|
@<Subr...@>= |
static void bignum_times_ten @,@,@[ARGS((bignum*))@]; |
static void bignum_dec @,@,@[ARGS((bignum*,bignum*,tetra))@]; |
static int bignum_compare @,@,@[ARGS((bignum*,bignum*))@]; |
void print_float @,@,@[ARGS((octa))@];@+@t}\6{@> |
void print_float(x) |
octa x; |
{ |
@<Local variables for |print_float|@>; |
if (x.h&sign_bit) printf("-"); |
@<Extract the exponent |e| and determine the |
fraction interval $[f\dts g]$ or $(f\dts g)$@>; |
@<Store $f$ and $g$ as multiprecise integers@>; |
@<Compute the significant digits |s| and decimal exponent |e|@>; |
@<Print the significant digits with proper context@>; |
} |
|
@ One way to visualize the problem being solved here is to consider |
the vastly simpler case in which there are only 2-bit exponents |
and 2-bit fractions. Then the sixteen possible 4-bit combinations |
have the following interpretations: |
$$\def\\{\;\dts\;} |
\vbox{\halign{#\qquad&$#$\hfil\cr |
0000&[0\\0.125]\cr |
0001&(0.125\\0.375)\cr |
0010&[0.375\\0.625]\cr |
0011&(0.625\\0.875)\cr |
0100&[0.875\\1.125]\cr |
0101&(1.125\\1.375)\cr |
0110&[1.375\\1.625]\cr |
0111&(1.625\\1.875)\cr |
1000&[1.875\\2.25]\cr |
1001&(2.25\\2.75)\cr |
1010&[2.75\\3.25]\cr |
1011&(3.25\\3.75)\cr |
1100&[3.75\\\infty]\cr |
1101&\rm NaN(0\\0.375)\cr |
1110&\rm NaN[0.375\\0.625]\cr |
1111&\rm NaN(0.625\\1)\cr}}$$ |
Notice that the interval is closed, $[f\dts g]$, when the fraction part |
is even; it is open, $(f\dts g)$, when the fraction part is odd. |
The printed outputs for these sixteen values, if we actually were |
dealing with such short exponents and fractions, would be |
\.{0.}, \.{.2}, \.{.5}, \.{.7}, \.{1.}, \.{1.2}, \.{1.5}, \.{1.7}, |
\.{2.}, \.{2.5}, \.{3.}, \.{3.5}, \.{Inf}, \.{NaN.2}, \.{NaN}, \.{NaN.8}, |
respectively. |
|
@<Extract the exponent |e|...@>= |
f=shift_left(x,1); |
e=f.h>>21; |
f.h&=0x1fffff; |
if (!f.h && !f.l) @<Handle the special case when the fraction part is zero@>@; |
else { |
g=incr(f,1); |
f=incr(f,-1); |
if (!e) e=1; /* subnormal */ |
else if (e==0x7ff) { |
printf("NaN"); |
if (g.h==0x100000 && g.l==1) return; /* the ``standard'' NaN */ |
e=0x3ff; /* extreme NaNs come out OK even without adjusting |f| or |g| */ |
}@+else f.h|=0x200000, g.h|=0x200000; |
} |
|
@ @<Local variables for |print_float|@>= |
octa f,g; /* lower and upper bounds on the fraction part */ |
register int e; /* exponent part */ |
register int j,k; /* all purpose indices */ |
|
@ The transition points between exponents correspond to powers of~2. At |
such points the interval extends only half as far to the left of that |
power of~2 as it does to the right. For example, in the 4-bit minifloat numbers |
considered above, case 1000 corresponds to the interval $[1.875\;\dts\;2.25]$. |
|
@<Handle the special case when the fraction part is zero@>= |
{ |
if (!e) { |
printf("0.");@+return; |
} |
if (e==0x7ff) { |
printf("Inf");@+return; |
} |
e--; |
f.h=0x3fffff, f.l=0xffffffff; |
g.h=0x400000, g.l=2; |
} |
|
@ We want to find the ``simplest'' value in the interval corresponding |
to the given number, in the sense that it has fewest significant |
digits when expressed in decimal notation. Thus, for example, |
if the floating point number can be described by a relatively |
short string such as `\.{.1}' or `\.{37e100}', we want to discover that |
representation. |
|
The basic idea is to generate the decimal representations of the |
two endpoints of the interval, outputting the leading digits where |
both endpoints agree, then making a final decision at the first place where |
they disagree. |
|
The ``simplest'' value is not always unique. For example, in the |
case of 4-bit minifloat numbers we could represent the bit pattern 0001 as |
either \.{.2} or \.{.3}, and we could represent 1001 in five equally short |
ways: \.{2.3} or \.{2.4} or \.{2.5} or \.{2.6} or \.{2.7}. The |
algorithm below tries to choose the middle possibility in such cases. |
|
[A solution to the analogous problem for fixed-point representations, |
without the additional complication of round-to-even, was used by |
the author in the program for \TeX; see {\sl Beauty is Our Business\/} |
(Springer, 1990), 233--242.] |
@^Knuth, Donald Ervin@> |
|
Suppose we are given two fractions $f$ and $g$, where $0\le f<g<1$, and |
we want to compute the shortest decimal in the closed interval $[f\dts g]$. |
If $f=0$, we are done. Otherwise let $10f=d+f'$ and $10g=e+g'$, where |
$0\le f'<1$ and $0\le g'<1$. If $d<e$, we can terminate by outputting |
any of the digits $d+1$, \dots,~$e$; otherwise we output the |
common digit $d=e$, and repeat the process on the fractions $0\le f'<g'<1$. |
A similar procedure works with respect to the open interval $(f\dts g)$. |
|
@ The program below carries out the stated algorithm by using multiprecision |
arithmetic on 77-place integers with 28 bits each. This choice |
facilitates multiplication by~10, and allows us to deal with the whole range of |
floating binary numbers using fixed point arithmetic. We keep track of |
the leading and trailing digit positions so that trivial operations on |
zeros are avoided. |
|
If |f| points to a \&{bignum}, its radix-$2^{28}$ digits are |
|f->dat[0]| through |f->dat[76]|, from most significant to least significant. |
We assume that all digit positions are zero unless they lie in the |
subarray between indices |f->a| and |f->b|, inclusive. |
Furthermore, both |f->dat[f->a]| and |f->dat[f->b]| are nonzero, |
unless |f->a=f->b=bignum_prec-1|. |
|
The \&{bignum} data type can be used with any radix less than |
$2^{32}$; we will use it later with radix~$10^9$. The |dat| array |
is made large enough to accommodate both applications. |
|
@d bignum_prec 157 /* would be 77 if we cared only about |print_float| */ |
|
@<Other type...@>= |
typedef struct { |
int a; /* index of the most significant digit */ |
int b; /* index of the least significant digit; must be $\ge a$ */ |
tetra dat[bignum_prec]; /* the digits; undefined except between |a| and |b| */ |
} bignum; |
|
@ Here, for example, is how we go from $f$ to $10f$, assuming that |
overflow will not occur and that the radix is $2^{28}$: |
|
@<Subr...@>= |
static void bignum_times_ten(f) |
bignum *f; |
{ |
register tetra *p,*q; register tetra x,carry; |
for (p=&f->dat[f->b],q=&f->dat[f->a],carry=0; p>=q; p--) { |
x=*p*10+carry; |
*p=x&0xfffffff; |
carry=x>>28; |
} |
*p=carry; |
if (carry) f->a--; |
if (f->dat[f->b]==0 && f->b>f->a) f->b--; |
} |
|
@ And here is how we test whether $f<g$, $f=g$, or $f>g$, using any |
radix whatever: |
|
@<Subr...@>= |
static int bignum_compare(f,g) |
bignum *f,*g; |
{ |
register tetra *p,*pp,*q,*qq; |
if (f->a!=g->a) return f->a > g->a? -1: 1; |
pp=&f->dat[f->b], qq=&g->dat[g->b]; |
for (p=&f->dat[f->a],q=&g->dat[g->a]; p<=pp; p++,q++) { |
if (*p!=*q) return *p<*q? -1: 1; |
if (q==qq) return p<pp; |
} |
return -1; |
} |
|
@ The following subroutine subtracts $g$ from~$f$, assuming that |
$f\ge g>0$ and using a given radix. |
|
@<Subr...@>= |
static void bignum_dec(f,g,r) |
bignum *f,*g; |
tetra r; /* the radix */ |
{ |
register tetra *p,*q,*qq; |
register int x,borrow; |
while (g->b>f->b) f->dat[++f->b]=0; |
qq=&g->dat[g->a]; |
for (p=&f->dat[g->b],q=&g->dat[g->b],borrow=0;q>=qq;p--,q--) { |
x=*p - *q - borrow; |
if (x>=0) borrow=0, *p=x; |
else borrow=1, *p=x+r; |
} |
for (;borrow;p--) |
if (*p) borrow=0, *p=*p-1; |
else *p=r-1; |
while (f->dat[f->a]==0) { |
if (f->a==f->b) { /* the result is zero */ |
f->a=f->b=bignum_prec-1, f->dat[bignum_prec-1]=0; |
return; |
} |
f->a++; |
} |
while (f->dat[f->b]==0) f->b--; |
} |
|
@ Armed with these subroutines, we are ready to solve the problem. |
The first task is to put the numbers into \&{bignum} form. |
If the exponent is |e|, the number destined for digit |dat[k]| will |
consist of the rightmost 28 bits of the given fraction after it has |
been shifted right $c-e-28k$ bits, for some constant~$c$. |
We choose $c$ so that, |
when $e$ has its maximum value \Hex{7ff}, the leading digit will |
go into position |dat[1]|, and so that when the number to be printed |
is exactly~1 the integer part of~$g$ will also be exactly~1. |
|
@d magic_offset 2112 /* the constant $c$ that makes it work */ |
@d origin 37 /* the radix point follows |dat[37]| */ |
|
@<Store $f$ and $g$ as multiprecise integers@>= |
k=(magic_offset-e)/28; |
ff.dat[k-1]=shift_right(f,magic_offset+28-e-28*k,1).l&0xfffffff; |
gg.dat[k-1]=shift_right(g,magic_offset+28-e-28*k,1).l&0xfffffff; |
ff.dat[k]=shift_right(f,magic_offset-e-28*k,1).l&0xfffffff; |
gg.dat[k]=shift_right(g,magic_offset-e-28*k,1).l&0xfffffff; |
ff.dat[k+1]=shift_left(f,e+28*k-(magic_offset-28)).l&0xfffffff; |
gg.dat[k+1]=shift_left(g,e+28*k-(magic_offset-28)).l&0xfffffff; |
ff.a=(ff.dat[k-1]? k-1: k); |
ff.b=(ff.dat[k+1]? k+1: k); |
gg.a=(gg.dat[k-1]? k-1: k); |
gg.b=(gg.dat[k+1]? k+1: k); |
|
@ If $e$ is sufficiently small, the fractions $f$ and $g$ will be less than~1, |
and we can use the stated algorithm directly. Of course, if $e$ is |
extremely small, a lot of leading zeros need to be lopped off; in the |
worst case, we may have to multiply $f$ and~$g$ by~10 more than 300 times. |
But hey, we don't need to do that extremely often, and computers are |
pretty fast nowadays. |
|
In the small-exponent case, the computation always terminates before |
$f$ becomes zero, because the interval endpoints are fractions with |
denominator $2^t$ for some $t>50$. |
|
The invariant relations |ff.dat[ff.a]!=0| and |gg.dat[gg.a]!=0| are |
not maintained by the computation here, when |ff.a=origin| or |gg.a=origin|. |
But no harm is done, because |bignum_compare| is not used. |
|
@<Compute the significant digits |s|...@>= |
if (e>0x401) @<Compute the significant digits in the large-exponent case@>@; |
else@+{ /* if |e<=0x401| we have |gg.a>=origin| and |gg.dat[origin]<=8| */ |
if (ff.a>origin) ff.dat[origin]=0; |
for (e=1, p=s; gg.a>origin || ff.dat[origin]==gg.dat[origin]; ) { |
if (gg.a>origin) e--; |
else *p++=ff.dat[origin]+'0', ff.dat[origin]=0, gg.dat[origin]=0; |
bignum_times_ten(&ff); |
bignum_times_ten(&gg); |
} |
*p++=((ff.dat[origin]+1+gg.dat[origin])>>1)+'0'; /* the middle digit */ |
} |
*p='\0'; /* terminate the string |s| */ |
|
@ When |e| is large, we use the stated algorithm by considering $f$ and |
$g$ to be fractions whose denominator is a power of~10. |
|
An interesting case arises when the number to be converted is |
\Hex{44ada56a4b0835bf}, since the interval turns out to be |
$$ (69999999999999991611392\ \ \dts\ \ 70000000000000000000000).$$ |
If this were a closed interval, we could simply give the answer |
\.{7e22}; but the number \.{7e22} actually corresponds to |
\Hex{44ada56a4b0835c0} |
because of the round-to-even rule. Therefore the correct answer is, say, |
\.{6.9999999999999995e22}. This example shows that we need a slightly |
different strategy in the case of open intervals; we cannot simply |
look at the first position in which the endpoints have different |
decimal digits. Therefore we change the invariant relation to $0\le f<g\le 1$, |
when open intervals are involved, |
and we do not terminate the process when $f=0$ or $g=1$. |
|
@<Compute the significant digits in the large-exponent case@>= |
{@+register int open=x.l&1; |
tt.dat[origin]=10; |
tt.a=tt.b=origin; |
for (e=1;bignum_compare(&gg,&tt)>=open;e++) |
bignum_times_ten(&tt); |
p=s; |
while (1) { |
bignum_times_ten(&ff); |
bignum_times_ten(&gg); |
for (j='0';bignum_compare(&ff,&tt)>=0;j++) |
bignum_dec(&ff,&tt,0x10000000),bignum_dec(&gg,&tt,0x10000000); |
if (bignum_compare(&gg,&tt)>=open) break; |
*p++=j; |
if (ff.a==bignum_prec-1 && !open) |
goto done; /* $f=0$ in a closed interval */ |
} |
for (k=j;bignum_compare(&gg,&tt)>=open;k++) bignum_dec(&gg,&tt,0x10000000); |
*p++=(j+1+k)>>1; /* the middle digit */ |
done:; |
} |
|
@ The length of string~|s| will be at most 17. For if $f$ and $g$ |
agree to 17 places, we have $g/f<1+10^{-16}$; but the |
ratio $g/f$ is always $\ge(1+2^{-52}+2^{-53})/(1+2^{-52}-2^{-53}) |
>1+2\times10^{-16}$. |
|
@<Local variables for |print_float|@>= |
bignum ff,gg; /* fractions or numerators of fractions */ |
bignum tt; /* power of ten (used as the denominator) */ |
char s[18]; |
register char *p; |
|
@ At this point the significant digits are in string |s|, and |s[0]!='0'|. |
If we put a decimal point at the left of~|s|, the result should |
be multiplied by $10^e$. |
|
We prefer the output `\.{300.}' to the form `\.{3e2}', and we prefer |
`\.{.03}' to `\.{3e-2}'. In general, the output will use an |
explicit exponent only if the alternative would take more than |
18~characters. |
|
@<Print the significant digits with proper context@>= |
if (e>17 || e<(int)strlen(s)-17) |
printf("%c%s%se%d",s[0],(s[1]? ".": ""),s+1,e-1); |
else if (e<0) printf(".%0*d%s",-e,0,s); |
else if (strlen(s)>=e) printf("%.*s.%s",e,s,s+e); |
else printf("%s%0*d.",s,e-(int)strlen(s),0); |
|
@*Floating point input conversion. Going the other way, we want to |
be able to convert a given decimal number into its floating binary |
@^decimal-to-binary conversion@> |
@^radix conversion@> |
@^multiprecision conversion@> |
equivalent. The following syntax is supported: |
$$\vbox{\halign{$#$\hfil\cr |
\<digit>\is\.0\mid\.1\mid\.2\mid\.3\mid\.4\mid |
\.5\mid\.6\mid\.7\mid\.8\mid\.9\cr |
\<digit string>\is\<digit>\mid\<digit string>\<digit>\cr |
\<decimal string>\is\<digit string>\..\mid\..\<digit string>\mid |
\<digit string>\..\<digit string>\cr |
\<optional sign>\is\<empty>\mid\.+\mid\.-\cr |
\<exponent>\is\.e\<optional sign>\<digit string>\cr |
\<optional exponent>\is\<empty>\mid\<exponent>\cr |
\<floating magnitude>\is\<digit string>\<exponent>\mid |
\<decimal string>\<optional exponent>\mid\cr |
\hskip12em \.{Inf}\mid\.{NaN}\mid\.{NaN.}\<digit string>\cr |
\<floating constant>\is\<optional sign>\<floating magnitude>\cr |
\<decimal constant>\is\<optional sign>\<digit string>\cr |
}}$$ |
For example, `\.{-3.}' is the floating constant \Hex{c008000000000000}\thinspace; |
`\.{1e3}' and `\.{1000}' are both equivalent to \Hex{408f400000000000}\thinspace; |
`\.{NaN}' and `\.{+NaN.5}' are both equivalent to \Hex{7ff8000000000000}. |
|
The |scan_const| routine looks at a given string and finds the |
longest initial substring that matches the syntax of either \<decimal |
constant> or \<floating constant>. It puts the corresponding value |
into the global octabyte variable~|val|; it also puts the position of the first |
unscanned character in the global pointer variable |next_char|. |
It returns 1 if a floating constant was found, 0~if a decimal constant |
was found, $-1$ if nothing was found. A decimal constant that doesn't |
fit in an octabyte is computed modulo~$2^{64}$. |
@^syntax of floating point constants@> |
|
The value of |exceptions| set by |scan_const| is not necessarily correct. |
|
@<Subr...@>= |
static void bignum_double @,@,@[ARGS((bignum*))@]; |
int scan_const @,@,@[ARGS((char*))@];@+@t}\6{@> |
int scan_const(s) |
char *s; |
{ |
@<Local variables for |scan_const|@>; |
val.h=val.l=0; |
p=s; |
if (*p=='+' || *p=='-') sign=*p++;@+else sign='+'; |
if (strncmp(p,"NaN",3)==0) NaN=true, p+=3; |
else NaN=false; |
if ((isdigit(*p)&&!NaN) || (*p=='.' && isdigit(*(p+1)))) |
@<Scan a number and |return|@>; |
if (NaN) @<Return the standard NaN@>; |
if (strncmp(p,"Inf",3)==0) @<Return infinity@>; |
no_const_found: next_char=s;@+return -1; |
} |
|
@ @<Glob...@>= |
octa val; /* value returned by |scan_const| */ |
char *next_char; /* pointer returned by |scan_const| */ |
|
@ @<Local variables for |scan_const|@>= |
register char *p,*q; /* for string manipulations */ |
register bool NaN; /* are we processing a NaN? */ |
int sign; /* |'+'| or |'-'| */ |
|
@ @<Return the standard NaN@>= |
{ |
next_char=p; |
val.h=0x600000, exp=0x3fe; |
goto packit; |
} |
|
@ @<Return infinity@>= |
{ |
next_char=p+3; |
goto make_it_infinite; |
} |
|
@ We saw above that a string of at most 17 digits is enough to characterize |
a floating point number, for purposes of output. But a much longer buffer |
for digits is needed when we're doing input. For example, consider the |
borderline quantity $(1+2^{-53})/2^{1022}$; its decimal expansion, when |
written out exactly, is a number with more than 750 significant digits: |
\.{2.2250738585...8125e-308}. |
If {\it any one\/} of those digits is increased, or if |
additional nonzero digits are added as in |
\.{2.2250738585...81250000001e-308}, |
the rounded value is supposed to change from \Hex{0010000000000000} |
to \Hex{0010000000000001}. |
|
We assume here that the user prefers a perfectly correct answer to |
a speedy almost-correct one, so we implement the most general case. |
|
@<Scan a number...@>= |
{ |
for (q=buf0,dec_pt=(char*)0;isdigit(*p);p++) { |
val=oplus(val,shift_left(val,2)); /* multiply by 5 */ |
val=incr(shift_left(val,1),*p-'0'); |
if (q>buf0 || *p!='0') |
if (q<buf_max) *q++=*p; |
else if (*(q-1)=='0') *(q-1)=*p; |
} |
if (NaN) *q++='1'; |
if (*p=='.') @<Scan a fraction part@>; |
next_char=p; |
if (*p=='e' && !NaN) @<Scan an exponent@>@; |
else exp=0; |
if (dec_pt) @<Return a floating point constant@>; |
if (sign=='-') val=ominus(zero_octa,val); |
return 0; |
} |
|
@ @<Scan a fraction part@>= |
{ |
dec_pt=q; |
p++; |
for (zeros=0;isdigit(*p);p++) |
if (*p=='0' && q==buf0) zeros++; |
else if (q<buf_max) *q++=*p; |
else if (*(q-1)=='0') *(q-1)=*p; |
} |
|
@ The buffer needs room for eight digits of padding at the left, followed |
by up to $1022+53-307$ significant digits, followed by a ``sticky'' digit |
at position |buf_max-1|, and eight more digits of padding. |
|
@d buf0 (buf+8) |
@d buf_max (buf+777) |
|
@<Glob...@>= |
static char buf[785]="00000000"; /* where we put significant input digits */ |
|
@ @<Local variables for |scan_const|@>= |
register char* dec_pt; /* position of decimal point in |buf| */ |
register int exp; /* scanned exponent; later used for raw binary exponent */ |
register int zeros; /* leading zeros removed after decimal point */ |
|
@ Here we don't advance |next_char| and force a decimal point until we |
know that a syntactically correct exponent exists. |
|
The code here will convert extra-large inputs like |
`\.{9e+9999999999999999}' into $\infty$ and extra-small inputs into zero. |
Strange inputs like `\.{-00.0e9999999}' must also be accommodated. |
|
@<Scan an exponent@>= |
{@+register char exp_sign; |
p++; |
if (*p=='+' || *p=='-') exp_sign=*p++;@+else exp_sign='+'; |
if (isdigit(*p)) { |
for (exp=*p++ -'0';isdigit(*p);p++) |
if (exp<1000) exp = 10*exp + *p - '0'; |
if (!dec_pt) dec_pt=q, zeros=0; |
if (exp_sign=='-') exp=-exp; |
next_char=p; |
} |
} |
|
@ @<Return a floating point constant@>= |
{ |
@<Move the digits from |buf| to |ff|@>; |
@<Determine the binary fraction and binary exponent@>; |
packit: @<Pack and round the answer@>; |
return 1; |
} |
|
@ Now we get ready to compute the binary fraction bits, by putting the |
scanned input digits into a multiprecision fixed-point |
accumulator |ff| that spans the full necessary range. |
After this step, the number that we want to convert to floating binary |
will appear in |ff.dat[ff.a]|, |ff.dat[ff.a+1]|, \dots, |
|ff.dat[ff.b]|. |
The radix-$10^9$ digit in ${\it ff}[36-k]$ is understood to be multiplied |
by $10^{9k}$, for $36\ge k\ge-120$. |
|
@<Move the digits from |buf| to |ff|@>= |
x=buf+341+zeros-dec_pt-exp; |
if (q==buf0 || x>=1413) { |
make_it_zero: exp=-99999;@+ goto packit; |
} |
if (x<0) { |
make_it_infinite: exp=99999;@+ goto packit; |
} |
ff.a=x/9; |
for (p=q;p<q+8;p++) *p='0'; /* pad with trailing zeros */ |
q=q-1-(q+341+zeros-dec_pt-exp)%9; /* compute stopping place in |buf| */ |
for (p=buf0-x%9,k=ff.a;p<=q && k<=156; p+=9, k++) |
@<Put the 9-digit number |*p|\thinspace\dots\thinspace|*(p+8)| |
into |ff.dat[k]|@>; |
ff.b=k-1; |
for (x=0;p<=q;p+=9) if (strncmp(p,"000000000",9)!=0) x=1; |
ff.dat[156]+=x; /* nonzero digits that fall off the right are sticky */ |
@^sticky bit@> |
while (ff.dat[ff.b]==0) ff.b--; |
|
@ @<Put the 9-digit number...@>= |
{ |
for (x=*p-'0',pp=p+1;pp<p+9;pp++) x=10*x + *pp - '0'; |
ff.dat[k]=x; |
} |
|
@ @<Local variables for |scan_const|@>= |
register int k,x; |
register char *pp; |
bignum ff,tt; |
|
@ Here's a subroutine that is dual to |bignum_times_ten|. It changes $f$ |
to~$2f$, assuming that overflow will not occur and that the radix is $10^9$. |
|
@<Subr...@>= |
static void bignum_double(f) |
bignum *f; |
{ |
register tetra *p,*q; register int x,carry; |
for (p=&f->dat[f->b],q=&f->dat[f->a],carry=0; p>=q; p--) { |
x = *p + *p + carry; |
if (x>=1000000000) carry=1, *p=x-1000000000; |
else carry=0, *p=x; |
} |
*p=carry; |
if (carry) f->a--; |
if (f->dat[f->b]==0 && f->b>f->a) f->b--; |
} |
|
@ @<Determine the binary fraction and binary exponent@>= |
val=zero_octa; |
if (ff.a>36) { |
for (exp=0x3fe;ff.a>36;exp--) bignum_double(&ff); |
for (k=54;k;k--) { |
if (ff.dat[36]) { |
if (k>=32) val.h |= 1<<(k-32);@+else val.l |= 1<<k; |
ff.dat[36]=0; |
if (ff.b==36) break; /* break if |ff| now zero */ |
} |
bignum_double(&ff); |
} |
}@+else { |
tt.a=tt.b=36, tt.dat[36]=2; |
for (exp=0x3fe;bignum_compare(&ff,&tt)>=0;exp++) bignum_double(&tt); |
for (k=54;k;k--) { |
bignum_double(&ff); |
if (bignum_compare(&ff,&tt)>=0) { |
if (k>=32) val.h |= 1<<(k-32);@+else val.l |= 1<<k; |
bignum_dec(&ff,&tt,1000000000); |
if (ff.a==bignum_prec-1) break; /* break if |ff| now zero */ |
} |
} |
} |
if (k==0) val.l |= 1; /* add sticky bit if |ff| nonzero */ |
|
@ We need to be careful that the input `\.{NaN.999999999999999999999}' doesn't |
get rounded up; it is supposed to yield \Hex{7fffffffffffffff}. |
|
Although the input `\.{NaN.0}' is illegal, strictly speaking, we silently |
convert it to \Hex{7ff0000000000001}---a number that would be |
output as `\.{NaN.0000000000000002}'. |
|
@<Pack and round the answer@>= |
val=fpack(val,exp,sign,ROUND_NEAR); |
if (NaN) { |
if ((val.h&0x7fffffff)==0x40000000) val.h |= 0x7fffffff, val.l=0xffffffff; |
else if ((val.h&0x7fffffff)==0x3ff00000 && !val.l) val.h|=0x40000000,val.l=1; |
else val.h |= 0x40000000; |
} |
|
@*Floating point remainders. In this section we implement the remainder |
of the floating point operations---one of which happens to be the |
operation of taking the remainder. |
|
The easiest task remaining is to compare two floating point quantities. |
Routine |fcomp| returns $-1$~if~$y<z$, 0~if~$y=z$, $+1$~if~$y>z$, and |
$+2$~if $y$ and~$z$ are unordered. |
|
@<Subr...@>= |
int fcomp @,@,@[ARGS((octa,octa))@];@+@t}\6{@> |
int fcomp(y,z) |
octa y,z; |
{ |
ftype yt,zt; |
int ye,ze; |
char ys,zs; |
octa yf,zf; |
register int x; |
yt=funpack(y,&yf,&ye,&ys); |
zt=funpack(z,&zf,&ze,&zs); |
switch (4*yt+zt) { |
case 4*nan+nan: case 4*zro+nan: case 4*num+nan: case 4*inf+nan: |
case 4*nan+zro: case 4*nan+num: case 4*nan+inf: return 2; |
case 4*zro+zro: return 0; |
case 4*zro+num: case 4*num+zro: case 4*zro+inf: case 4*inf+zro: |
case 4*num+num: case 4*num+inf: case 4*inf+num: case 4*inf+inf: |
if (ys!=zs) x=1; |
else if (y.h>z.h) x=1; |
else if (y.h<z.h) x=-1; |
else if (y.l>z.l) x=1; |
else if (y.l<z.l) x=-1; |
else return 0; |
break; |
} |
return (ys=='-'? -x: x); |
} |
|
@ Several \MMIX\ operations act on a single floating point number and |
accept an arbitrary rounding mode. For example, consider the |
operation of rounding to the nearest floating point integer: |
|
@<Subr...@>= |
octa fintegerize @,@,@[ARGS((octa,int))@];@+@t}\6{@> |
octa fintegerize(z,r) |
octa z; /* the operand */ |
int r; /* the rounding mode */ |
{ |
ftype zt; |
int ze; |
char zs; |
octa xf,zf; |
zt=funpack(z,&zf,&ze,&zs); |
if (!r) r=cur_round; |
switch (zt) { |
case nan:@+if (!(z.h&0x80000)) {@+exceptions|=I_BIT;@+z.h|=0x80000;@+} |
case inf: case zro: return z; |
case num: @<Integerize and |return|@>; |
} |
} |
|
@ @<Integerize...@>= |
if (ze>=1074) return fpack(zf,ze,zs,ROUND_OFF); /* already an integer */ |
if (ze<=1020) xf.h=0,xf.l=1; |
else {@+octa oo; |
xf=shift_right(zf,1074-ze,1); |
oo=shift_left(xf,1074-ze); |
if (oo.l!=zf.l || oo.h!=zf.h) xf.l|=1; /* sticky bit */ |
@^sticky bit@> |
} |
switch (r) { |
case ROUND_DOWN:@+ if (zs=='-') xf=incr(xf,3);@+break; |
case ROUND_UP:@+ if (zs!='-') xf=incr(xf,3); |
case ROUND_OFF: break; |
case ROUND_NEAR: xf=incr(xf, xf.l&4? 2: 1);@+break; |
} |
xf.l&=0xfffffffc; |
if (ze>=1022) return fpack(shift_left(xf,1074-ze),ze,zs,ROUND_OFF); |
if (xf.l) xf.h=0x3ff00000, xf.l=0; |
if (zs=='-') xf.h|=sign_bit; |
return xf; |
|
@ To convert floating point to fixed point, we use |fixit|. |
|
@<Subr...@>= |
octa fixit @,@,@[ARGS((octa,int))@];@+@t}\6{@> |
octa fixit(z,r) |
octa z; /* the operand */ |
int r; /* the rounding mode */ |
{ |
ftype zt; |
int ze; |
char zs; |
octa zf,o; |
zt=funpack(z,&zf,&ze,&zs); |
if (!r) r=cur_round; |
switch (zt) { |
case nan: case inf: exceptions|=I_BIT;@+return z; |
case zro: return zero_octa; |
case num:@+if (funpack(fintegerize(z,r),&zf,&ze,&zs)==zro) return zero_octa; |
if (ze<=1076) o=shift_right(zf,1076-ze,1); |
else { |
if (ze>1085 || (ze==1085 && (zf.h>0x400000 || @| |
(zf.h==0x400000 && (zf.l || zs!='-'))))) exceptions|=W_BIT; |
if (ze>=1140) return zero_octa; |
o=shift_left(zf,ze-1076); |
} |
return (zs=='-'? ominus(zero_octa,o): o); |
} |
} |
|
@ Going the other way, we can specify not only a rounding mode but whether |
the given fixed point octabyte is signed or unsigned, and whether the |
result should be rounded to short precision. |
|
@<Subr...@>= |
octa floatit @,@,@[ARGS((octa,int,int,int))@];@+@t}\6{@> |
octa floatit(z,r,u,p) |
octa z; /* octabyte to float */ |
int r; /* rounding mode */ |
int u; /* unsigned? */ |
int p; /* short precision? */ |
{ |
int e;@+char s; |
register int t; |
exceptions=0; |
if (!z.h && !z.l) return zero_octa; |
if (!r) r=cur_round; |
if (!u && (z.h&sign_bit)) s='-', z=ominus(zero_octa,z);@+ else s='+'; |
e=1076; |
while (z.h<0x400000) e--,z=shift_left(z,1); |
while (z.h>=0x800000) { |
e++; |
t=z.l&1; |
z=shift_right(z,1,1); |
z.l|=t; |
} |
if (p) @<Convert to short float@>; |
return fpack(z,e,s,r); |
} |
|
@ @<Convert to short float@>= |
{ |
register int ex;@+register tetra t; |
t=sfpack(z,e,s,r); |
ex=exceptions; |
sfunpack(t,&z,&e,&s); |
exceptions=ex; |
} |
|
@ The square root operation is more interesting. |
|
@<Subr...@>= |
octa froot @,@,@[ARGS((octa,int))@];@+@t}\6{@> |
octa froot(z,r) |
octa z; /* the operand */ |
int r; /* the rounding mode */ |
{ |
ftype zt; |
int ze; |
char zs; |
octa x,xf,rf,zf; |
register int xe,k; |
if (!r) r=cur_round; |
zt=funpack(z,&zf,&ze,&zs); |
if (zs=='-' && zt!=zro) exceptions|=I_BIT, x=standard_NaN; |
else@+switch (zt) { |
case nan:@+ if (!(z.h&0x80000)) exceptions|=I_BIT, z.h|=0x80000; |
return z; |
case inf: case zro: x=z;@+break; |
case num: @<Take the square root and |return|@>; |
} |
if (zs=='-') x.h|=sign_bit; |
return x; |
} |
|
@ The square root can be found by an adaptation of the old pencil-and-paper |
method. If $n=\lfloor\sqrt s\rfloor$, where $s$ is an integer, |
we have $s=n^2+r$ where $0\le r\le2n$; |
this invariant can be maintained if we replace $s$ by $4s+(0,1,2,3)$ |
and $n$ by $2n+(0,1)$. The following code implements this idea with |
$2n$ in~|xf| and $r$ in~|rf|. (It could easily be made to run about |
twice as fast.) |
|
@<Take the square root and |return|@>= |
xf.h=0, xf.l=2; |
xe=(ze+0x3fe)>>1; |
if (ze&1) zf=shift_left(zf,1); |
rf.h=0, rf.l=(zf.h>>22)-1; |
for (k=53;k;k--) { |
rf=shift_left(rf,2);@+ xf=shift_left(xf,1); |
if (k>=43) rf=incr(rf,(zf.h>>(2*(k-43)))&3); |
else if (k>=27) rf=incr(rf,(zf.l>>(2*(k-27)))&3); |
if ((rf.l>xf.l && rf.h>=xf.h) || rf.h>xf.h) { |
xf.l++;@+rf=ominus(rf,xf);@+xf.l++; |
} |
} |
if (rf.h || rf.l) xf.l++; /* sticky bit */ |
return fpack(xf,xe,'+',r); |
|
@ And finally, the genuine floating point remainder. Subroutine |fremstep| |
either calculates $y\,{\rm rem}\,z$ or reduces $y$ to a smaller number |
having the same remainder with respect to~$z$. In the latter case |
the |E_BIT| is set in |exceptions|. A third parameter, |delta|, |
gives a decrease in exponent that is acceptable for incomplete results; |
if |delta| is sufficiently large, say 2500, the correct result will |
always be obtained in one step of |fremstep|. |
|
@<Subr...@>= |
octa fremstep @,@,@[ARGS((octa,octa,int))@];@+@t}\6{@> |
octa fremstep(y,z,delta) |
octa y,z; |
int delta; |
{ |
ftype yt,zt; |
int ye,ze; |
char xs,ys,zs; |
octa x,xf,yf,zf; |
register int xe,thresh,odd; |
yt=funpack(y,&yf,&ye,&ys); |
zt=funpack(z,&zf,&ze,&zs); |
switch (4*yt+zt) { |
@t\4@>@<The usual NaN cases@>; |
case 4*zro+zro: case 4*num+zro: case 4*inf+zro: |
case 4*inf+num: case 4*inf+inf: x=standard_NaN; |
exceptions|=I_BIT;@+break; |
case 4*zro+num: case 4*zro+inf: case 4*num+inf: return y; |
case 4*num+num: @<Remainderize nonzero numbers and |return|@>; |
zero_out: x=zero_octa; |
} |
if (ys=='-') x.h|=sign_bit; |
return x; |
} |
|
@ If there's a huge difference in exponents and the remainder is nonzero, |
this computation will take a long time. One could compute |
$(2^ny)\,{\rm rem}\,z$ much more quickly for large~$n$ by using $O(\log n)$ |
multiplications modulo~$z$, but the floating remainder operation isn't |
important enough to justify such expensive hardware. |
|
Results of floating remainder are always exact, so the rounding mode |
is immaterial. |
|
@<Remainderize...@>= |
odd=0; /* will be 1 if we've subtracted an odd multiple of~$z$ from $y$ */ |
thresh=ye-delta; |
if (thresh<ze) thresh=ze; |
while (ye>=thresh) @<Reduce |(ye,yf)| by a multiple of |zf|; |
|goto zero_out| if the remainder is zero, |
|goto try_complement| if appropriate@>; |
if (ye>=ze) { |
exceptions|=E_BIT;@+return fpack(yf,ye,ys,ROUND_OFF); |
} |
if (ye<ze-1) return fpack(yf,ye,ys,ROUND_OFF); |
yf=shift_right(yf,1,1); |
try_complement: xf=ominus(zf,yf), xe=ze, xs='+' + '-' - ys; |
if (xf.h>yf.h || (xf.h==yf.h && (xf.l>yf.l || (xf.l==yf.l && !odd)))) |
xf=yf, xs=ys; |
while (xf.h<0x400000) xe--, xf=shift_left(xf,1); |
return fpack(xf,xe,xs,ROUND_OFF); |
|
@ Here we are careful not to change the sign of |y|, because a remainder |
of~0 is supposed to inherit the original sign of~|y|. |
|
@<Reduce |(ye,yf)| by a multiple of |zf|...@>= |
{ |
if (yf.h==zf.h && yf.l==zf.l) goto zero_out; |
if (yf.h<zf.h || (yf.h==zf.h && yf.l<zf.l)) { |
if (ye==ze) goto try_complement; |
ye--, yf=shift_left(yf,1); |
} |
yf=ominus(yf,zf); |
if (ye==ze) odd=1; |
while (yf.h<0x400000) ye--, yf=shift_left(yf,1); |
} |
|
@* Index. |
|
/fp.c
0,0 → 1,2216
#define sign_bit ((unsigned) 0x80000000) \ |
|
#define ROUND_OFF 1 |
#define ROUND_UP 2 |
#define ROUND_DOWN 3 |
#define ROUND_NEAR 4 \ |
|
#define X_BIT (1<<8) |
#define Z_BIT (1<<9) |
#define U_BIT (1<<10) |
#define O_BIT (1<<11) |
#define I_BIT (1<<12) |
#define W_BIT (1<<13) |
#define V_BIT (1<<14) |
#define D_BIT (1<<15) |
#define E_BIT (1<<18) \ |
|
#define zero_exponent (-1000) \ |
|
#define bignum_prec 157 \ |
|
#define magic_offset 2112 |
#define origin 37 \ |
|
#define buf0 (buf+8) |
#define buf_max (buf+777) \ |
|
/*1:*/ |
#line 32 "./mmix-arith.w" |
|
#include <stdio.h> |
#include <string.h> |
#include <ctype.h> |
/*2:*/ |
#line 49 "./mmix-arith.w" |
|
#ifdef __STDC__ |
#define ARGS(list) list |
#else |
#define ARGS(list) () |
#endif |
|
/*:2*/ |
#line 36 "./mmix-arith.w" |
|
typedef enum |
{ false, true } bool; |
/*3:*/ |
#line 60 "./mmix-arith.w" |
|
typedef unsigned int tetra; |
|
typedef struct |
{ |
tetra h, l; |
} octa; |
|
/*:3*/ |
#line 38 "./mmix-arith.w" |
|
/*36:*/ |
#line 605 "./mmix-arith.w" |
|
typedef enum |
{ zro, num, inf, nan } ftype; |
|
/*:36*//*59: */ |
#line 1110 "./mmix-arith.w" |
|
typedef struct |
{ |
int a; |
int b; |
tetra dat[bignum_prec]; |
} bignum; |
|
/*:59*/ |
#line 39 "./mmix-arith.w" |
|
/*4:*/ |
#line 67 "./mmix-arith.w" |
|
octa zero_octa; |
octa neg_one = { -1, -1 }; |
octa inf_octa = { 0x7ff00000, 0 }; |
octa standard_NaN = { 0x7ff80000, 0 }; |
octa aux; |
bool overflow; |
|
/*:4*//*9: */ |
#line 174 "./mmix-arith.w" |
|
extern octa aux; |
extern bool overflow; |
|
/*:9*//*30: */ |
#line 464 "./mmix-arith.w" |
|
int cur_round; |
|
/*:30*//*32: */ |
#line 528 "./mmix-arith.w" |
|
int exceptions; |
|
/*:32*//*69: */ |
#line 1359 "./mmix-arith.w" |
|
octa val; |
char *next_char; |
|
/*:69*//*75: */ |
#line 1432 "./mmix-arith.w" |
|
static char buf[785] = "00000000"; |
|
/*:75*/ |
#line 40 "./mmix-arith.w" |
|
/*5:*/ |
#line 78 "./mmix-arith.w" |
|
octa oplus ARGS ((octa, octa)); |
octa |
oplus (y, z) |
octa y, z; |
{ |
octa x; |
x.h = y.h + z.h; |
x.l = y.l + z.l; |
if (x.l < y.l) |
x.h++; |
return x; |
} |
|
octa ominus ARGS ((octa, octa)); |
octa |
ominus (y, z) |
octa y, z; |
{ |
octa x; |
x.h = y.h - z.h; |
x.l = y.l - z.l; |
if (x.l > y.l) |
x.h--; |
return x; |
} |
|
/*:5*//*6: */ |
#line 102 "./mmix-arith.w" |
|
octa incr ARGS ((octa, int)); |
octa |
incr (y, delta) |
octa y; |
int delta; |
{ |
octa x; |
x.h = y.h; |
x.l = y.l + delta; |
if (delta >= 0) |
{ |
if (x.l < y.l) |
x.h++; |
} |
else if (x.l > y.l) |
x.h--; |
return x; |
} |
|
/*:6*//*7: */ |
#line 117 "./mmix-arith.w" |
|
octa shift_left ARGS ((octa, int)); |
octa |
shift_left (y, s) |
octa y; |
int s; |
{ |
while (s >= 32) |
y.h = y.l, y.l = 0, s -= 32; |
if (s) |
{ |
register tetra yhl = y.h << s, ylh = y.l >> (32 - s); |
y.h = yhl + ylh; |
y.l <<= s; |
} |
return y; |
} |
|
octa shift_right ARGS ((octa, int, int)); |
octa |
shift_right (y, s, u) |
octa y; |
int s, u; |
{ |
while (s >= 32) |
y.l = y.h, y.h = (u ? 0 : -(y.h >> 31)), s -= 32; |
if (s) |
{ |
register tetra yhl = y.h << (32 - s), ylh = y.l >> s; |
y.h = (u ? 0 : (-(y.h >> 31)) << (32 - s)) + (y.h >> s); |
y.l = yhl + ylh; |
} |
return y; |
} |
|
/*:7*//*8: */ |
#line 150 "./mmix-arith.w" |
|
octa omult ARGS ((octa, octa)); |
octa |
omult (y, z) |
octa y, z; |
{ |
register int i, j, k; |
tetra u[4], v[4], w[8]; |
register tetra t; |
octa acc; |
/*10:*/ |
#line 178 "./mmix-arith.w" |
|
u[3] = y.h >> 16, u[2] = y.h & 0xffff, u[1] = y.l >> 16, u[0] = |
y.l & 0xffff; |
v[3] = z.h >> 16, v[2] = z.h & 0xffff, v[1] = z.l >> 16, v[0] = |
z.l & 0xffff; |
|
/*:10*/ |
#line 159 "./mmix-arith.w" |
; |
for (j = 0; j < 4; j++) |
w[j] = 0; |
for (j = 0; j < 4; j++) |
if (!v[j]) |
w[j + 4] = 0; |
else |
{ |
for (i = k = 0; i < 4; i++) |
{ |
t = u[i] * v[j] + w[i + j] + k; |
w[i + j] = t & 0xffff, k = t >> 16; |
} |
w[j + 4] = k; |
} |
/*11:*/ |
#line 182 "./mmix-arith.w" |
|
aux.h = (w[7] << 16) + w[6], aux.l = (w[5] << 16) + w[4]; |
acc.h = (w[3] << 16) + w[2], acc.l = (w[1] << 16) + w[0]; |
|
/*:11*/ |
#line 170 "./mmix-arith.w" |
; |
return acc; |
} |
|
/*:8*//*12: */ |
#line 191 "./mmix-arith.w" |
|
octa signed_omult ARGS ((octa, octa)); |
octa |
signed_omult (y, z) |
octa y, z; |
{ |
octa acc; |
acc = omult (y, z); |
if (y.h & sign_bit) |
aux = ominus (aux, z); |
if (z.h & sign_bit) |
aux = ominus (aux, y); |
overflow = (aux.h != aux.l || (aux.h ^ (aux.h >> 1) ^ (acc.h & sign_bit))); |
return acc; |
} |
|
/*:12*//*13: */ |
#line 215 "./mmix-arith.w" |
|
octa odiv ARGS ((octa, octa, octa)); |
octa |
odiv (x, y, z) |
octa x, y, z; |
{ |
register int i, j, k, n, d; |
tetra u[8], v[4], q[4], mask, qhat, rhat, vh, vmh; |
register tetra t; |
octa acc; |
/*14:*/ |
#line 234 "./mmix-arith.w" |
|
if (x.h > z.h || (x.h == z.h && x.l >= z.l)) |
{ |
aux = y; |
return x; |
} |
|
/*:14*/ |
#line 224 "./mmix-arith.w" |
; |
/*15:*/ |
#line 239 "./mmix-arith.w" |
|
u[7] = x.h >> 16, u[6] = x.h & 0xffff, u[5] = x.l >> 16, u[4] = |
x.l & 0xffff; |
u[3] = y.h >> 16, u[2] = y.h & 0xffff, u[1] = y.l >> 16, u[0] = |
y.l & 0xffff; |
v[3] = z.h >> 16, v[2] = z.h & 0xffff, v[1] = z.l >> 16, v[0] = |
z.l & 0xffff; |
|
/*:15*/ |
#line 225 "./mmix-arith.w" |
; |
/*16:*/ |
#line 244 "./mmix-arith.w" |
|
for (n = 4; v[n - 1] == 0; n--); |
|
/*:16*/ |
#line 226 "./mmix-arith.w" |
; |
/*17:*/ |
#line 250 "./mmix-arith.w" |
|
vh = v[n - 1]; |
for (d = 0; vh < 0x8000; d++, vh <<= 1); |
for (j = k = 0; j < n + 4; j++) |
{ |
t = (u[j] << d) + k; |
u[j] = t & 0xffff, k = t >> 16; |
} |
for (j = k = 0; j < n; j++) |
{ |
t = (v[j] << d) + k; |
v[j] = t & 0xffff, k = t >> 16; |
} |
vh = v[n - 1]; |
vmh = (n > 1 ? v[n - 2] : 0); |
|
/*:17*/ |
#line 227 "./mmix-arith.w" |
; |
for (j = 3; j >= 0; j--) /*20: */ |
#line 276 "./mmix-arith.w" |
|
{ |
/*21:*/ |
#line 284 "./mmix-arith.w" |
|
t = (u[j + n] << 16) + u[j + n - 1]; |
qhat = t / vh, rhat = t - vh * qhat; |
if (n > 1) |
while (qhat == 0x10000 || qhat * vmh > (rhat << 16) + u[j + n - 2]) |
{ |
qhat--, rhat += vh; |
if (rhat >= 0x10000) |
break; |
} |
|
/*:21*/ |
#line 278 "./mmix-arith.w" |
; |
/*22:*/ |
#line 296 "./mmix-arith.w" |
|
for (i = k = 0; i < n; i++) |
{ |
t = u[i + j] + 0xffff0000 - k - qhat * v[i]; |
u[i + j] = t & 0xffff, k = 0xffff - (t >> 16); |
} |
|
/*:22*/ |
#line 279 "./mmix-arith.w" |
; |
/*23:*/ |
#line 305 "./mmix-arith.w" |
|
if (u[j + n] != k) |
{ |
qhat--; |
for (i = k = 0; i < n; i++) |
{ |
t = u[i + j] + v[i] + k; |
u[i + j] = t & 0xffff, k = t >> 16; |
} |
} |
|
/*:23*/ |
#line 280 "./mmix-arith.w" |
; |
q[j] = qhat; |
} |
|
/*:20*/ |
#line 228 "./mmix-arith.w" |
; |
/*18:*/ |
#line 264 "./mmix-arith.w" |
|
mask = (1 << d) - 1; |
for (j = 3; j >= n; j--) |
u[j] = 0; |
for (k = 0; j >= 0; j--) |
{ |
t = (k << 16) + u[j]; |
u[j] = t >> d, k = t & mask; |
} |
|
/*:18*/ |
#line 229 "./mmix-arith.w" |
; |
/*19:*/ |
#line 272 "./mmix-arith.w" |
|
acc.h = (q[3] << 16) + q[2], acc.l = (q[1] << 16) + q[0]; |
aux.h = (u[3] << 16) + u[2], aux.l = (u[1] << 16) + u[0]; |
|
/*:19*/ |
#line 230 "./mmix-arith.w" |
; |
return acc; |
} |
|
/*:13*//*24: */ |
#line 317 "./mmix-arith.w" |
|
octa signed_odiv ARGS ((octa, octa)); |
octa |
signed_odiv (y, z) |
octa y, z; |
{ |
octa yy, zz, q; |
register int sy, sz; |
if (y.h & sign_bit) |
sy = 2, yy = ominus (zero_octa, y); |
else |
sy = 0, yy = y; |
if (z.h & sign_bit) |
sz = 1, zz = ominus (zero_octa, z); |
else |
sz = 0, zz = z; |
q = odiv (zero_octa, yy, zz); |
overflow = false; |
switch (sy + sz) |
{ |
case 2 + 1: |
aux = ominus (zero_octa, aux); |
if (q.h == sign_bit) |
overflow = true; |
case 0 + 0: |
return q; |
case 2 + 0: |
if (aux.h || aux.l) |
aux = ominus (zz, aux); |
goto negate_q; |
case 0 + 1: |
if (aux.h || aux.l) |
aux = ominus (aux, zz); |
negate_q:if (aux.h || aux.l) |
return ominus (neg_one, q); |
else |
return ominus (zero_octa, q); |
} |
} |
|
/*:24*//*25: */ |
#line 346 "./mmix-arith.w" |
|
octa oand ARGS ((octa, octa)); |
octa |
oand (y, z) |
octa y, z; |
{ |
octa x; |
x.h = y.h & z.h; |
x.l = y.l & z.l; |
return x; |
} |
|
octa oandn ARGS ((octa, octa)); |
octa |
oandn (y, z) |
octa y, z; |
{ |
octa x; |
x.h = y.h & ~z.h; |
x.l = y.l & ~z.l; |
return x; |
} |
|
octa oxor ARGS ((octa, octa)); |
octa |
oxor (y, z) |
octa y, z; |
{ |
octa x; |
x.h = y.h ^ z.h; |
x.l = y.l ^ z.l; |
return x; |
} |
|
/*:25*//*26: */ |
#line 387 "./mmix-arith.w" |
|
int count_bits ARGS ((tetra)); |
int |
count_bits (x) |
tetra x; |
{ |
register int xx = x; |
xx = xx - ((xx >> 1) & 0x55555555); |
xx = (xx & 0x33333333) + ((xx >> 2) & 0x33333333); |
xx = (xx + (xx >> 4)) & 0x0f0f0f0f; |
xx = xx + (xx >> 8); |
return (xx + (xx >> 16)) & 0xff; |
} |
|
/*:26*//*27: */ |
#line 403 "./mmix-arith.w" |
|
tetra byte_diff ARGS ((tetra, tetra)); |
tetra |
byte_diff (y, z) |
tetra y, z; |
{ |
register tetra d = (y & 0x00ff00ff) + 0x01000100 - (z & 0x00ff00ff); |
register tetra m = d & 0x01000100; |
register tetra x = d & (m - (m >> 8)); |
d = ((y >> 8) & 0x00ff00ff) + 0x01000100 - ((z >> 8) & 0x00ff00ff); |
m = d & 0x01000100; |
return x + ((d & (m - (m >> 8))) << 8); |
} |
|
/*:27*//*28: */ |
#line 421 "./mmix-arith.w" |
|
tetra wyde_diff ARGS ((tetra, tetra)); |
tetra |
wyde_diff (y, z) |
tetra y, z; |
{ |
register tetra a = ((y >> 16) - (z >> 16)) & 0x10000; |
register tetra b = ((y & 0xffff) - (z & 0xffff)) & 0x10000; |
return y - (z ^ ((y ^ z) & (b - a - (b >> 16)))); |
} |
|
/*:28*//*29: */ |
#line 434 "./mmix-arith.w" |
|
octa bool_mult ARGS ((octa, octa, bool)); |
octa |
bool_mult (y, z, xor) |
octa y, z; |
bool xor; |
{ |
octa o, x; |
register tetra a, b, c; |
register int k; |
for (k = 0, o = y, x = zero_octa; o.h || o.l; |
k++, o = shift_right (o, 8, 1)) |
if (o.l & 0xff) |
{ |
a = ((z.h >> k) & 0x01010101) * 0xff; |
b = ((z.l >> k) & 0x01010101) * 0xff; |
c = (o.l & 0xff) * 0x01010101; |
if (xor) |
x.h ^= a & c, x.l ^= b & c; |
else |
x.h |= a & c, x.l |= b & c; |
} |
return x; |
} |
|
/*:29*//*31: */ |
#line 503 "./mmix-arith.w" |
|
octa fpack ARGS ((octa, int, char, int)); |
octa |
fpack (f, e, s, r) |
octa f; |
int e; |
char s; |
int r; |
{ |
octa o; |
if (e > 0x7fd) |
e = 0x7ff, o = zero_octa; |
else |
{ |
if (e < 0) |
{ |
if (e < -54) |
o.h = 0, o.l = 1; |
else |
{ |
octa oo; |
o = shift_right (f, -e, 1); |
oo = shift_left (o, -e); |
if (oo.l != f.l || oo.h != f.h) |
o.l |= 1; |
|
} |
e = 0; |
} |
else |
o = f; |
} |
/*33:*/ |
#line 533 "./mmix-arith.w" |
|
if (o.l & 3) |
exceptions |= X_BIT; |
switch (r) |
{ |
case ROUND_DOWN: |
if (s == '-') |
o = incr (o, 3); |
break; |
case ROUND_UP: |
if (s != '-') |
o = incr (o, 3); |
case ROUND_OFF: |
break; |
case ROUND_NEAR: |
o = incr (o, o.l & 4 ? 2 : 1); |
break; |
} |
o = shift_right (o, 2, 1); |
o.h += e << 20; |
if (o.h >= 0x7ff00000) |
exceptions |= O_BIT + X_BIT; |
else if (o.h < 0x100000) |
exceptions |= U_BIT; |
if (s == '-') |
o.h |= sign_bit; |
return o; |
|
/*:33*/ |
#line 525 "./mmix-arith.w" |
; |
} |
|
/*:31*//*34: */ |
#line 551 "./mmix-arith.w" |
|
tetra sfpack ARGS ((octa, int, char, int)); |
tetra |
sfpack (f, e, s, r) |
octa f; |
int e; |
char s; |
int r; |
{ |
register tetra o; |
if (e > 0x47d) |
e = 0x47f, o = 0; |
else |
{ |
o = shift_left (f, 3).h; |
if (f.l & 0x1fffffff) |
o |= 1; |
if (e < 0x380) |
{ |
if (e < 0x380 - 25) |
o = 1; |
else |
{ |
register tetra o0, oo; |
o0 = o; |
o = o >> (0x380 - e); |
oo = o << (0x380 - e); |
if (oo != o0) |
o |= 1; |
|
} |
e = 0x380; |
} |
} |
/*35:*/ |
#line 579 "./mmix-arith.w" |
|
if (o & 3) |
exceptions |= X_BIT; |
switch (r) |
{ |
case ROUND_DOWN: |
if (s == '-') |
o += 3; |
break; |
case ROUND_UP: |
if (s != '-') |
o += 3; |
case ROUND_OFF: |
break; |
case ROUND_NEAR: |
o += (o & 4 ? 2 : 1); |
break; |
} |
o = o >> 2; |
o += (e - 0x380) << 23; |
if (o >= 0x7f800000) |
exceptions |= O_BIT + X_BIT; |
else if (o < 0x100000) |
exceptions |= U_BIT; |
if (s == '-') |
o |= sign_bit; |
return o; |
|
/*:35*/ |
#line 576 "./mmix-arith.w" |
; |
} |
|
/*:34*//*37: */ |
#line 608 "./mmix-arith.w" |
|
ftype funpack ARGS ((octa, octa *, int *, char *)); |
ftype |
funpack (x, f, e, s) |
octa x; |
octa *f; |
int *e; |
char *s; |
{ |
register int ee; |
exceptions = 0; |
*s = (x.h & sign_bit ? '-' : '+'); |
*f = shift_left (x, 2); |
f->h &= 0x3fffff; |
ee = (x.h >> 20) & 0x7ff; |
if (ee) |
{ |
*e = ee - 1; |
f->h |= 0x400000; |
return (ee < 0x7ff ? num : f->h == 0x400000 && !f->l ? inf : nan); |
} |
if (!x.l && !f->h) |
{ |
*e = zero_exponent; |
return zro; |
} |
do |
{ |
ee--; |
*f = shift_left (*f, 1); |
} |
while (!(f->h & 0x400000)); |
*e = ee; |
return num; |
} |
|
/*:37*//*38: */ |
#line 634 "./mmix-arith.w" |
|
ftype sfunpack ARGS ((tetra, octa *, int *, char *)); |
ftype |
sfunpack (x, f, e, s) |
tetra x; |
octa *f; |
int *e; |
char *s; |
{ |
register int ee; |
exceptions = 0; |
*s = (x & sign_bit ? '-' : '+'); |
f->h = (x >> 1) & 0x3fffff, f->l = x << 31; |
ee = (x >> 23) & 0xff; |
if (ee) |
{ |
*e = ee + 0x380 - 1; |
f->h |= 0x400000; |
return (ee < 0xff ? num : (x & 0x7fffffff) == 0x7f800000 ? inf : nan); |
} |
if (!(x & 0x7fffffff)) |
{ |
*e = zero_exponent; |
return zro; |
} |
do |
{ |
ee--; |
*f = shift_left (*f, 1); |
} |
while (!(f->h & 0x400000)); |
*e = ee + 0x380; |
return num; |
} |
|
/*:38*//*39: */ |
#line 663 "./mmix-arith.w" |
|
octa load_sf ARGS ((tetra)); |
octa |
load_sf (z) |
tetra z; |
{ |
octa f, x; |
int e; |
char s; |
ftype t; |
t = sfunpack (z, &f, &e, &s); |
switch (t) |
{ |
case zro: |
x = zero_octa; |
break; |
case num: |
return fpack (f, e, s, ROUND_OFF); |
case inf: |
x = inf_octa; |
break; |
case nan: |
x = shift_right (f, 2, 1); |
x.h |= 0x7ff00000; |
break; |
} |
if (s == '-') |
x.h |= sign_bit; |
return x; |
} |
|
/*:39*//*40: */ |
#line 680 "./mmix-arith.w" |
|
tetra store_sf ARGS ((octa)); |
tetra |
store_sf (x) |
octa x; |
{ |
octa f; |
tetra z; |
int e; |
char s; |
ftype t; |
t = funpack (x, &f, &e, &s); |
switch (t) |
{ |
case zro: |
z = 0; |
break; |
case num: |
return sfpack (f, e, s, cur_round); |
case inf: |
z = 0x7f800000; |
break; |
case nan: |
if (!(f.h & 0x200000)) |
{ |
f.h |= 0x200000; |
exceptions |= I_BIT; |
} |
z = 0x7f800000 | (f.h << 1) | (f.l >> 31); |
break; |
} |
if (s == '-') |
z |= sign_bit; |
return z; |
} |
|
/*:40*//*41: */ |
#line 705 "./mmix-arith.w" |
|
octa fmult ARGS ((octa, octa)); |
octa |
fmult (y, z) |
octa y, z; |
{ |
ftype yt, zt; |
int ye, ze; |
char ys, zs; |
octa x, xf, yf, zf; |
register int xe; |
register char xs; |
yt = funpack (y, &yf, &ye, &ys); |
zt = funpack (z, &zf, &ze, &zs); |
xs = ys + zs - '+'; |
switch (4 * yt + zt) |
{ |
/*42:*/ |
#line 731 "./mmix-arith.w" |
|
case 4 * nan + nan: |
if (!(y.h & 0x80000)) |
exceptions |= I_BIT; |
case 4 * zro + nan: |
case 4 * num + nan: |
case 4 * inf + nan: |
if (!(z.h & 0x80000)) |
exceptions |= I_BIT, z.h |= 0x80000; |
return z; |
case 4 * nan + zro: |
case 4 * nan + num: |
case 4 * nan + inf: |
if (!(y.h & 0x80000)) |
exceptions |= I_BIT, y.h |= 0x80000; |
return y; |
|
/*:42*/ |
#line 720 "./mmix-arith.w" |
; |
case 4 * zro + zro: |
case 4 * zro + num: |
case 4 * num + zro: |
x = zero_octa; |
break; |
case 4 * num + inf: |
case 4 * inf + num: |
case 4 * inf + inf: |
x = inf_octa; |
break; |
case 4 * zro + inf: |
case 4 * inf + zro: |
x = standard_NaN; |
exceptions |= I_BIT; |
break; |
case 4 * num + num: /*43: */ |
#line 740 "./mmix-arith.w" |
|
xe = ye + ze - 0x3fd; |
x = omult (yf, shift_left (zf, 9)); |
if (aux.h >= 0x400000) |
xf = aux; |
else |
xf = shift_left (aux, 1), xe--; |
if (x.h || x.l) |
xf.l |= 1; |
return fpack (xf, xe, xs, cur_round); |
|
/*:43*/ |
#line 725 "./mmix-arith.w" |
; |
} |
if (xs == '-') |
x.h |= sign_bit; |
return x; |
} |
|
/*:41*//*44: */ |
#line 748 "./mmix-arith.w" |
|
octa fdivide ARGS ((octa, octa)); |
octa |
fdivide (y, z) |
octa y, z; |
{ |
ftype yt, zt; |
int ye, ze; |
char ys, zs; |
octa x, xf, yf, zf; |
register int xe; |
register char xs; |
yt = funpack (y, &yf, &ye, &ys); |
zt = funpack (z, &zf, &ze, &zs); |
xs = ys + zs - '+'; |
switch (4 * yt + zt) |
{ |
/*42:*/ |
#line 731 "./mmix-arith.w" |
|
case 4 * nan + nan: |
if (!(y.h & 0x80000)) |
exceptions |= I_BIT; |
case 4 * zro + nan: |
case 4 * num + nan: |
case 4 * inf + nan: |
if (!(z.h & 0x80000)) |
exceptions |= I_BIT, z.h |= 0x80000; |
return z; |
case 4 * nan + zro: |
case 4 * nan + num: |
case 4 * nan + inf: |
if (!(y.h & 0x80000)) |
exceptions |= I_BIT, y.h |= 0x80000; |
return y; |
|
/*:42*/ |
#line 763 "./mmix-arith.w" |
; |
case 4 * zro + inf: |
case 4 * zro + num: |
case 4 * num + inf: |
x = zero_octa; |
break; |
case 4 * num + zro: |
exceptions |= Z_BIT; |
case 4 * inf + num: |
case 4 * inf + zro: |
x = inf_octa; |
break; |
case 4 * zro + zro: |
case 4 * inf + inf: |
x = standard_NaN; |
exceptions |= I_BIT; |
break; |
case 4 * num + num: /*45: */ |
#line 775 "./mmix-arith.w" |
|
xe = ye - ze + 0x3fd; |
xf = odiv (yf, zero_octa, shift_left (zf, 9)); |
if (xf.h >= 0x800000) |
{ |
aux.l |= xf.l & 1; |
xf = shift_right (xf, 1, 1); |
xe++; |
} |
if (aux.h || aux.l) |
xf.l |= 1; |
return fpack (xf, xe, xs, cur_round); |
|
/*:45*/ |
#line 769 "./mmix-arith.w" |
; |
} |
if (xs == '-') |
x.h |= sign_bit; |
return x; |
} |
|
/*:44*//*46: */ |
#line 790 "./mmix-arith.w" |
|
octa fplus ARGS ((octa, octa)); |
octa |
fplus (y, z) |
octa y, z; |
{ |
ftype yt, zt; |
int ye, ze; |
char ys, zs; |
octa x, xf, yf, zf; |
register int xe, d; |
register char xs; |
yt = funpack (y, &yf, &ye, &ys); |
zt = funpack (z, &zf, &ze, &zs); |
switch (4 * yt + zt) |
{ |
/*42:*/ |
#line 731 "./mmix-arith.w" |
|
case 4 * nan + nan: |
if (!(y.h & 0x80000)) |
exceptions |= I_BIT; |
case 4 * zro + nan: |
case 4 * num + nan: |
case 4 * inf + nan: |
if (!(z.h & 0x80000)) |
exceptions |= I_BIT, z.h |= 0x80000; |
return z; |
case 4 * nan + zro: |
case 4 * nan + num: |
case 4 * nan + inf: |
if (!(y.h & 0x80000)) |
exceptions |= I_BIT, y.h |= 0x80000; |
return y; |
|
/*:42*/ |
#line 804 "./mmix-arith.w" |
; |
case 4 * zro + num: |
return fpack (zf, ze, zs, ROUND_OFF); |
break; |
case 4 * num + zro: |
return fpack (yf, ye, ys, ROUND_OFF); |
break; |
case 4 * inf + inf: |
if (ys != zs) |
{ |
exceptions |= I_BIT; |
x = standard_NaN; |
xs = zs; |
break; |
} |
case 4 * num + inf: |
case 4 * zro + inf: |
x = inf_octa; |
xs = zs; |
break; |
case 4 * inf + num: |
case 4 * inf + zro: |
x = inf_octa; |
xs = ys; |
break; |
case 4 * num + num: |
if (y.h != (z.h ^ 0x80000000) || y.l != z.l) |
/*47:*/ |
#line 821 "./mmix-arith.w" |
|
{ |
octa o, oo; |
if (ye < ze |
|| (ye == ze && (yf.h < zf.h || (yf.h == zf.h && yf.l < zf.l)))) |
/*48:*/ |
#line 839 "./mmix-arith.w" |
|
{ |
o = yf, yf = zf, zf = o; |
d = ye, ye = ze, ze = d; |
d = ys, ys = zs, zs = d; |
} |
|
/*:48*/ |
#line 824 "./mmix-arith.w" |
; |
d = ye - ze; |
xs = ys, xe = ye; |
if (d) /*49: */ |
#line 859 "./mmix-arith.w" |
|
{ |
if (d <= 2) |
zf = shift_right (zf, d, 1); |
else if (d > 53) |
zf.h = 0, zf.l = 1; |
else |
{ |
if (ys != zs) |
d--, xe--, yf = shift_left (yf, 1); |
o = zf; |
zf = shift_right (o, d, 1); |
oo = shift_left (zf, d); |
if (oo.l != o.l || oo.h != o.h) |
zf.l |= 1; |
} |
} |
|
/*:49*/ |
#line 827 "./mmix-arith.w" |
; |
if (ys == zs) |
{ |
xf = oplus (yf, zf); |
if (xf.h >= 0x800000) |
xe++, d = xf.l & 1, xf = shift_right (xf, 1, 1), xf.l |= d; |
} |
else |
{ |
xf = ominus (yf, zf); |
if (xf.h >= 0x800000) |
xe++, d = xf.l & 1, xf = shift_right (xf, 1, 1), xf.l |= d; |
else |
while (xf.h < 0x400000) |
xe--, xf = shift_left (xf, 1); |
} |
return fpack (xf, xe, xs, cur_round); |
} |
|
/*:47*/ |
#line 813 "./mmix-arith.w" |
; |
case 4 * zro + zro: |
x = zero_octa; |
xs = (ys == zs ? ys : cur_round == ROUND_DOWN ? '-' : '+'); |
break; |
} |
if (xs == '-') |
x.h |= sign_bit; |
return x; |
} |
|
/*:46*//*50: */ |
#line 883 "./mmix-arith.w" |
|
int fepscomp ARGS ((octa, octa, octa, int)); |
int |
fepscomp (y, z, e, s) |
octa y, z, e; |
int s; |
{ |
octa yf, zf, ef, o, oo; |
int ye, ze, ee; |
char ys, zs, es; |
register int yt, zt, et, d; |
et = funpack (e, &ef, &ee, &es); |
if (es == '-') |
return 2; |
switch (et) |
{ |
case nan: |
return 2; |
case inf: |
ee = 10000; |
case num: |
case zro: |
break; |
} |
yt = funpack (y, &yf, &ye, &ys); |
zt = funpack (z, &zf, &ze, &zs); |
switch (4 * yt + zt) |
{ |
case 4 * nan + nan: |
case 4 * nan + inf: |
case 4 * nan + num: |
case 4 * nan + zro: |
case 4 * inf + nan: |
case 4 * num + nan: |
case 4 * zro + nan: |
return 2; |
case 4 * inf + inf: |
return (ys == zs || ee >= 1023); |
case 4 * inf + num: |
case 4 * inf + zro: |
case 4 * num + inf: |
case 4 * zro + inf: |
return (s && ee >= 1022); |
case 4 * zro + zro: |
return 1; |
case 4 * zro + num: |
case 4 * num + zro: |
if (!s) |
return 0; |
case 4 * num + num: |
break; |
} |
/*51:*/ |
#line 919 "./mmix-arith.w" |
|
/*52:*/ |
#line 934 "./mmix-arith.w" |
|
if (ye < 0 && yt != zro) |
yf = shift_left (y, 2), ye = 0; |
if (ze < 0 && zt != zro) |
zf = shift_left (z, 2), ze = 0; |
|
/*:52*/ |
#line 920 "./mmix-arith.w" |
; |
if (ye < ze || (ye == ze && (yf.h < zf.h || (yf.h == zf.h && yf.l < zf.l)))) |
/*48:*/ |
#line 839 "./mmix-arith.w" |
|
{ |
o = yf, yf = zf, zf = o; |
d = ye, ye = ze, ze = d; |
d = ys, ys = zs, zs = d; |
} |
|
/*:48*/ |
#line 922 "./mmix-arith.w" |
; |
if (ze == zero_exponent) |
ze = ye; |
d = ye - ze; |
if (!s) |
ee -= d; |
if (ee >= 1023) |
return 1; |
/*53:*/ |
#line 956 "./mmix-arith.w" |
|
if (d > 54) |
o = zero_octa, oo = zf; |
else |
o = shift_right (zf, d, 1), oo = shift_left (o, d); |
if (oo.h != zf.h || oo.l != zf.l) |
{ |
if (ee < 1020) |
return 0; |
o = incr (o, ys == zs ? 0 : 1); |
} |
o = (ys == zs ? ominus (yf, o) : oplus (yf, o)); |
|
/*:53*/ |
#line 927 "./mmix-arith.w" |
; |
if (!o.h && !o.l) |
return 1; |
if (ee < 968) |
return 0; |
if (ee >= 1021) |
ef = shift_left (ef, ee - 1021); |
else |
ef = shift_right (ef, 1021 - ee, 1); |
return o.h < ef.h || (o.h == ef.h && o.l <= ef.l); |
|
/*:51*/ |
#line 912 "./mmix-arith.w" |
; |
} |
|
/*:50*//*54: */ |
#line 972 "./mmix-arith.w" |
|
static void bignum_times_ten ARGS ((bignum *)); |
static void bignum_dec ARGS ((bignum *, bignum *, tetra)); |
static int bignum_compare ARGS ((bignum *, bignum *)); |
void print_float ARGS ((octa)); |
void |
print_float (x) |
octa x; |
{ |
/*56:*/ |
#line 1035 "./mmix-arith.w" |
|
octa f, g; |
register int e; |
register int j, k; |
|
/*:56*//*66: */ |
#line 1281 "./mmix-arith.w" |
|
bignum ff, gg; |
bignum tt; |
char s[18]; |
register char *p; |
|
/*:66*/ |
#line 980 "./mmix-arith.w" |
; |
if (x.h & sign_bit) |
printf ("-"); |
/*55:*/ |
#line 1019 "./mmix-arith.w" |
|
f = shift_left (x, 1); |
e = f.h >> 21; |
f.h &= 0x1fffff; |
if (!f.h && !f.l) /*57: */ |
#line 1045 "./mmix-arith.w" |
|
{ |
if (!e) |
{ |
printf ("0."); |
return; |
} |
if (e == 0x7ff) |
{ |
printf ("Inf"); |
return; |
} |
e--; |
f.h = 0x3fffff, f.l = 0xffffffff; |
g.h = 0x400000, g.l = 2; |
} |
|
/*:57*/ |
#line 1023 "./mmix-arith.w" |
|
else |
{ |
g = incr (f, 1); |
f = incr (f, -1); |
if (!e) |
e = 1; |
else if (e == 0x7ff) |
{ |
printf ("NaN"); |
if (g.h == 0x100000 && g.l == 1) |
return; |
e = 0x3ff; |
} |
else |
f.h |= 0x200000, g.h |= 0x200000; |
} |
|
/*:55*/ |
#line 983 "./mmix-arith.w" |
; |
/*63:*/ |
#line 1195 "./mmix-arith.w" |
|
k = (magic_offset - e) / 28; |
ff.dat[k - 1] = |
shift_right (f, magic_offset + 28 - e - 28 * k, 1).l & 0xfffffff; |
gg.dat[k - 1] = |
shift_right (g, magic_offset + 28 - e - 28 * k, 1).l & 0xfffffff; |
ff.dat[k] = shift_right (f, magic_offset - e - 28 * k, 1).l & 0xfffffff; |
gg.dat[k] = shift_right (g, magic_offset - e - 28 * k, 1).l & 0xfffffff; |
ff.dat[k + 1] = |
shift_left (f, e + 28 * k - (magic_offset - 28)).l & 0xfffffff; |
gg.dat[k + 1] = |
shift_left (g, e + 28 * k - (magic_offset - 28)).l & 0xfffffff; |
ff.a = (ff.dat[k - 1] ? k - 1 : k); |
ff.b = (ff.dat[k + 1] ? k + 1 : k); |
gg.a = (gg.dat[k - 1] ? k - 1 : k); |
gg.b = (gg.dat[k + 1] ? k + 1 : k); |
|
/*:63*/ |
#line 984 "./mmix-arith.w" |
; |
/*64:*/ |
#line 1223 "./mmix-arith.w" |
|
if (e > 0x401) /*65: */ |
#line 1254 "./mmix-arith.w" |
|
{ |
register int open = x.l & 1; |
tt.dat[origin] = 10; |
tt.a = tt.b = origin; |
for (e = 1; bignum_compare (&gg, &tt) >= open; e++) |
bignum_times_ten (&tt); |
p = s; |
while (1) |
{ |
bignum_times_ten (&ff); |
bignum_times_ten (&gg); |
for (j = '0'; bignum_compare (&ff, &tt) >= 0; j++) |
bignum_dec (&ff, &tt, 0x10000000), bignum_dec (&gg, &tt, |
0x10000000); |
if (bignum_compare (&gg, &tt) >= open) |
break; |
*p++ = j; |
if (ff.a == bignum_prec - 1 && !open) |
goto done; |
} |
for (k = j; bignum_compare (&gg, &tt) >= open; k++) |
bignum_dec (&gg, &tt, 0x10000000); |
*p++ = (j + 1 + k) >> 1; |
done:; |
} |
|
/*:65*/ |
#line 1224 "./mmix-arith.w" |
|
else |
{ |
if (ff.a > origin) |
ff.dat[origin] = 0; |
for (e = 1, p = s; gg.a > origin || ff.dat[origin] == gg.dat[origin];) |
{ |
if (gg.a > origin) |
e--; |
else |
*p++ = ff.dat[origin] + '0', ff.dat[origin] = 0, gg.dat[origin] = |
0; |
bignum_times_ten (&ff); |
bignum_times_ten (&gg); |
} |
*p++ = ((ff.dat[origin] + 1 + gg.dat[origin]) >> 1) + '0'; |
} |
*p = '\0'; |
|
/*:64*/ |
#line 985 "./mmix-arith.w" |
; |
/*67:*/ |
#line 1296 "./mmix-arith.w" |
|
if (e > 17 || e < (int) strlen (s) - 17) |
printf ("%c%s%se%d", s[0], (s[1] ? "." : ""), s + 1, e - 1); |
else if (e < 0) |
printf (".%0*d%s", -e, 0, s); |
else if (strlen (s) >= e) |
printf ("%.*s.%s", e, s, s + e); |
else |
printf ("%s%0*d.", s, e - (int) strlen (s), 0); |
|
/*:67*/ |
#line 986 "./mmix-arith.w" |
; |
} |
|
/*:54*//*60: */ |
#line 1120 "./mmix-arith.w" |
|
static void |
bignum_times_ten (f) |
bignum *f; |
{ |
register tetra *p, *q; |
register tetra x, carry; |
for (p = &f->dat[f->b], q = &f->dat[f->a], carry = 0; p >= q; p--) |
{ |
x = *p * 10 + carry; |
*p = x & 0xfffffff; |
carry = x >> 28; |
} |
*p = carry; |
if (carry) |
f->a--; |
if (f->dat[f->b] == 0 && f->b > f->a) |
f->b--; |
} |
|
/*:60*//*61: */ |
#line 1138 "./mmix-arith.w" |
|
static int |
bignum_compare (f, g) |
bignum *f, *g; |
{ |
register tetra *p, *pp, *q, *qq; |
if (f->a != g->a) |
return f->a > g->a ? -1 : 1; |
pp = &f->dat[f->b], qq = &g->dat[g->b]; |
for (p = &f->dat[f->a], q = &g->dat[g->a]; p <= pp; p++, q++) |
{ |
if (*p != *q) |
return *p < *q ? -1 : 1; |
if (q == qq) |
return p < pp; |
} |
return -1; |
} |
|
/*:61*//*62: */ |
#line 1155 "./mmix-arith.w" |
|
static void |
bignum_dec (f, g, r) |
bignum *f, *g; |
tetra r; |
{ |
register tetra *p, *q, *qq; |
register int x, borrow; |
while (g->b > f->b) |
f->dat[++f->b] = 0; |
qq = &g->dat[g->a]; |
for (p = &f->dat[g->b], q = &g->dat[g->b], borrow = 0; q >= qq; p--, q--) |
{ |
x = *p - *q - borrow; |
if (x >= 0) |
borrow = 0, *p = x; |
else |
borrow = 1, *p = x + r; |
} |
for (; borrow; p--) |
if (*p) |
borrow = 0, *p = *p - 1; |
else |
*p = r - 1; |
while (f->dat[f->a] == 0) |
{ |
if (f->a == f->b) |
{ |
f->a = f->b = bignum_prec - 1, f->dat[bignum_prec - 1] = 0; |
return; |
} |
f->a++; |
} |
while (f->dat[f->b] == 0) |
f->b--; |
} |
|
/*:62*//*68: */ |
#line 1340 "./mmix-arith.w" |
|
static void bignum_double ARGS ((bignum *)); |
int scan_const ARGS ((char *)); |
int |
scan_const (s) |
char *s; |
{ |
/*70:*/ |
#line 1363 "./mmix-arith.w" |
|
register char *p, *q; |
register bool NaN; |
int sign; |
|
/*:70*//*76: */ |
#line 1435 "./mmix-arith.w" |
|
register char *dec_pt; |
register int exp; |
register int zeros; |
|
/*:76*//*81: */ |
#line 1503 "./mmix-arith.w" |
|
register int k, x; |
register char *pp; |
bignum ff, tt; |
|
/*:81*/ |
#line 1346 "./mmix-arith.w" |
; |
val.h = val.l = 0; |
p = s; |
if (*p == '+' || *p == '-') |
sign = *p++; |
else |
sign = '+'; |
if (strncmp (p, "NaN", 3) == 0) |
NaN = true, p += 3; |
else |
NaN = false; |
if ((isdigit (*p) && !NaN) || (*p == '.' && isdigit (*(p + 1)))) |
/*73:*/ |
#line 1396 "./mmix-arith.w" |
|
{ |
for (q = buf0, dec_pt = (char *) 0; isdigit (*p); p++) |
{ |
val = oplus (val, shift_left (val, 2)); |
val = incr (shift_left (val, 1), *p - '0'); |
if (q > buf0 || *p != '0') |
if (q < buf_max) |
*q++ = *p; |
else if (*(q - 1) == '0') |
*(q - 1) = *p; |
} |
if (NaN) |
*q++ = '1'; |
if (*p == '.') /*74: */ |
#line 1415 "./mmix-arith.w" |
|
{ |
dec_pt = q; |
p++; |
for (zeros = 0; isdigit (*p); p++) |
if (*p == '0' && q == buf0) |
zeros++; |
else if (q < buf_max) |
*q++ = *p; |
else if (*(q - 1) == '0') |
*(q - 1) = *p; |
} |
|
/*:74*/ |
#line 1406 "./mmix-arith.w" |
; |
next_char = p; |
if (*p == 'e' && !NaN) /*77: */ |
#line 1447 "./mmix-arith.w" |
|
{ |
register char exp_sign; |
p++; |
if (*p == '+' || *p == '-') |
exp_sign = *p++; |
else |
exp_sign = '+'; |
if (isdigit (*p)) |
{ |
for (exp = *p++ - '0'; isdigit (*p); p++) |
if (exp < 1000) |
exp = 10 * exp + *p - '0'; |
if (!dec_pt) |
dec_pt = q, zeros = 0; |
if (exp_sign == '-') |
exp = -exp; |
next_char = p; |
} |
} |
|
/*:77*/ |
#line 1408 "./mmix-arith.w" |
|
else |
exp = 0; |
if (dec_pt) /*78: */ |
#line 1460 "./mmix-arith.w" |
|
{ |
/*79:*/ |
#line 1477 "./mmix-arith.w" |
|
x = buf + 341 + zeros - dec_pt - exp; |
if (q == buf0 || x >= 1413) |
{ |
make_it_zero:exp = -99999; |
goto packit; |
} |
if (x < 0) |
{ |
make_it_infinite:exp = 99999; |
goto packit; |
} |
ff.a = x / 9; |
for (p = q; p < q + 8; p++) |
*p = '0'; |
q = q - 1 - (q + 341 + zeros - dec_pt - exp) % 9; |
for (p = buf0 - x % 9, k = ff.a; p <= q && k <= 156; p += 9, k++) |
/*80:*/ |
#line 1497 "./mmix-arith.w" |
|
{ |
for (x = *p - '0', pp = p + 1; pp < p + 9; pp++) |
x = 10 * x + *pp - '0'; |
ff.dat[k] = x; |
} |
|
/*:80*/ |
#line 1490 "./mmix-arith.w" |
; |
ff.b = k - 1; |
for (x = 0; p <= q; p += 9) |
if (strncmp (p, "000000000", 9) != 0) |
x = 1; |
ff.dat[156] += x; |
|
while (ff.dat[ff.b] == 0) |
ff.b--; |
|
/*:79*/ |
#line 1462 "./mmix-arith.w" |
; |
/*83:*/ |
#line 1526 "./mmix-arith.w" |
|
val = zero_octa; |
if (ff.a > 36) |
{ |
for (exp = 0x3fe; ff.a > 36; exp--) |
bignum_double (&ff); |
for (k = 54; k; k--) |
{ |
if (ff.dat[36]) |
{ |
if (k >= 32) |
val.h |= 1 << (k - 32); |
else |
val.l |= 1 << k; |
ff.dat[36] = 0; |
if (ff.b == 36) |
break; |
} |
bignum_double (&ff); |
} |
} |
else |
{ |
tt.a = tt.b = 36, tt.dat[36] = 2; |
for (exp = 0x3fe; bignum_compare (&ff, &tt) >= 0; exp++) |
bignum_double (&tt); |
for (k = 54; k; k--) |
{ |
bignum_double (&ff); |
if (bignum_compare (&ff, &tt) >= 0) |
{ |
if (k >= 32) |
val.h |= 1 << (k - 32); |
else |
val.l |= 1 << k; |
bignum_dec (&ff, &tt, 1000000000); |
if (ff.a == bignum_prec - 1) |
break; |
} |
} |
} |
if (k == 0) |
val.l |= 1; |
|
/*:83*/ |
#line 1463 "./mmix-arith.w" |
; |
packit: /*84: */ |
#line 1559 "./mmix-arith.w" |
|
val = fpack (val, exp, sign, ROUND_NEAR); |
if (NaN) |
{ |
if ((val.h & 0x7fffffff) == 0x40000000) |
val.h |= 0x7fffffff, val.l = 0xffffffff; |
else if ((val.h & 0x7fffffff) == 0x3ff00000 && !val.l) |
val.h |= 0x40000000, val.l = 1; |
else |
val.h |= 0x40000000; |
} |
|
/*:84*/ |
#line 1464 "./mmix-arith.w" |
; |
return 1; |
} |
|
/*:78*/ |
#line 1410 "./mmix-arith.w" |
; |
if (sign == '-') |
val = ominus (zero_octa, val); |
return 0; |
} |
|
/*:73*/ |
#line 1353 "./mmix-arith.w" |
; |
if (NaN) /*71: */ |
#line 1368 "./mmix-arith.w" |
|
{ |
next_char = p; |
val.h = 0x600000, exp = 0x3fe; |
goto packit; |
} |
|
/*:71*/ |
#line 1354 "./mmix-arith.w" |
; |
if (strncmp (p, "Inf", 3) == 0) /*72: */ |
#line 1375 "./mmix-arith.w" |
|
{ |
next_char = p + 3; |
goto make_it_infinite; |
} |
|
/*:72*/ |
#line 1355 "./mmix-arith.w" |
; |
no_const_found:next_char = s; |
return -1; |
} |
|
/*:68*//*82: */ |
#line 1511 "./mmix-arith.w" |
|
static void |
bignum_double (f) |
bignum *f; |
{ |
register tetra *p, *q; |
register int x, carry; |
for (p = &f->dat[f->b], q = &f->dat[f->a], carry = 0; p >= q; p--) |
{ |
x = *p + *p + carry; |
if (x >= 1000000000) |
carry = 1, *p = x - 1000000000; |
else |
carry = 0, *p = x; |
} |
*p = carry; |
if (carry) |
f->a--; |
if (f->dat[f->b] == 0 && f->b > f->a) |
f->b--; |
} |
|
/*:82*//*85: */ |
#line 1575 "./mmix-arith.w" |
|
int fcomp ARGS ((octa, octa)); |
int |
fcomp (y, z) |
octa y, z; |
{ |
ftype yt, zt; |
int ye, ze; |
char ys, zs; |
octa yf, zf; |
register int x; |
yt = funpack (y, &yf, &ye, &ys); |
zt = funpack (z, &zf, &ze, &zs); |
switch (4 * yt + zt) |
{ |
case 4 * nan + nan: |
case 4 * zro + nan: |
case 4 * num + nan: |
case 4 * inf + nan: |
case 4 * nan + zro: |
case 4 * nan + num: |
case 4 * nan + inf: |
return 2; |
case 4 * zro + zro: |
return 0; |
case 4 * zro + num: |
case 4 * num + zro: |
case 4 * zro + inf: |
case 4 * inf + zro: |
case 4 * num + num: |
case 4 * num + inf: |
case 4 * inf + num: |
case 4 * inf + inf: |
if (ys != zs) |
x = 1; |
else if (y.h > z.h) |
x = 1; |
else if (y.h < z.h) |
x = -1; |
else if (y.l > z.l) |
x = 1; |
else if (y.l < z.l) |
x = -1; |
else |
return 0; |
break; |
} |
return (ys == '-' ? -x : x); |
} |
|
/*:85*//*86: */ |
#line 1608 "./mmix-arith.w" |
|
octa fintegerize ARGS ((octa, int)); |
octa |
fintegerize (z, r) |
octa z; |
int r; |
{ |
ftype zt; |
int ze; |
char zs; |
octa xf, zf; |
zt = funpack (z, &zf, &ze, &zs); |
if (!r) |
r = cur_round; |
switch (zt) |
{ |
case nan: |
if (!(z.h & 0x80000)) |
{ |
exceptions |= I_BIT; |
z.h |= 0x80000; |
} |
case inf: |
case zro: |
return z; |
case num: /*87: */ |
#line 1627 "./mmix-arith.w" |
|
if (ze >= 1074) |
return fpack (zf, ze, zs, ROUND_OFF); |
if (ze <= 1020) |
xf.h = 0, xf.l = 1; |
else |
{ |
octa oo; |
xf = shift_right (zf, 1074 - ze, 1); |
oo = shift_left (xf, 1074 - ze); |
if (oo.l != zf.l || oo.h != zf.h) |
xf.l |= 1; |
|
} |
switch (r) |
{ |
case ROUND_DOWN: |
if (zs == '-') |
xf = incr (xf, 3); |
break; |
case ROUND_UP: |
if (zs != '-') |
xf = incr (xf, 3); |
case ROUND_OFF: |
break; |
case ROUND_NEAR: |
xf = incr (xf, xf.l & 4 ? 2 : 1); |
break; |
} |
xf.l &= 0xfffffffc; |
if (ze >= 1022) |
return fpack (shift_left (xf, 1074 - ze), ze, zs, ROUND_OFF); |
if (xf.l) |
xf.h = 0x3ff00000, xf.l = 0; |
if (zs == '-') |
xf.h |= sign_bit; |
return xf; |
|
/*:87*/ |
#line 1623 "./mmix-arith.w" |
; |
} |
} |
|
/*:86*//*88: */ |
#line 1650 "./mmix-arith.w" |
|
octa fixit ARGS ((octa, int)); |
octa |
fixit (z, r) |
octa z; |
int r; |
{ |
ftype zt; |
int ze; |
char zs; |
octa zf, o; |
zt = funpack (z, &zf, &ze, &zs); |
if (!r) |
r = cur_round; |
switch (zt) |
{ |
case nan: |
case inf: |
exceptions |= I_BIT; |
return z; |
case zro: |
return zero_octa; |
case num: |
if (funpack (fintegerize (z, r), &zf, &ze, &zs) == zro) |
return zero_octa; |
if (ze <= 1076) |
o = shift_right (zf, 1076 - ze, 1); |
else |
{ |
if (ze > 1085 || (ze == 1085 && (zf.h > 0x400000 || |
(zf.h == 0x400000 |
&& (zf.l || zs != '-'))))) |
exceptions |= W_BIT; |
if (ze >= 1140) |
return zero_octa; |
o = shift_left (zf, ze - 1076); |
} |
return (zs == '-' ? ominus (zero_octa, o) : o); |
} |
} |
|
/*:88*//*89: */ |
#line 1681 "./mmix-arith.w" |
|
octa floatit ARGS ((octa, int, int, int)); |
octa |
floatit (z, r, u, p) |
octa z; |
int r; |
int u; |
int p; |
{ |
int e; |
char s; |
register int t; |
exceptions = 0; |
if (!z.h && !z.l) |
return zero_octa; |
if (!r) |
r = cur_round; |
if (!u && (z.h & sign_bit)) |
s = '-', z = ominus (zero_octa, z); |
else |
s = '+'; |
e = 1076; |
while (z.h < 0x400000) |
e--, z = shift_left (z, 1); |
while (z.h >= 0x800000) |
{ |
e++; |
t = z.l & 1; |
z = shift_right (z, 1, 1); |
z.l |= t; |
} |
if (p) /*90: */ |
#line 1707 "./mmix-arith.w" |
|
{ |
register int ex; |
register tetra t; |
t = sfpack (z, e, s, r); |
ex = exceptions; |
sfunpack (t, &z, &e, &s); |
exceptions = ex; |
} |
|
/*:90*/ |
#line 1703 "./mmix-arith.w" |
; |
return fpack (z, e, s, r); |
} |
|
/*:89*//*91: */ |
#line 1718 "./mmix-arith.w" |
|
octa froot ARGS ((octa, int)); |
octa |
froot (z, r) |
octa z; |
int r; |
{ |
ftype zt; |
int ze; |
char zs; |
octa x, xf, rf, zf; |
register int xe, k; |
if (!r) |
r = cur_round; |
zt = funpack (z, &zf, &ze, &zs); |
if (zs == '-' && zt != zro) |
exceptions |= I_BIT, x = standard_NaN; |
else |
switch (zt) |
{ |
case nan: |
if (!(z.h & 0x80000)) |
exceptions |= I_BIT, z.h |= 0x80000; |
return z; |
case inf: |
case zro: |
x = z; |
break; |
case num: /*92: */ |
#line 1750 "./mmix-arith.w" |
|
xf.h = 0, xf.l = 2; |
xe = (ze + 0x3fe) >> 1; |
if (ze & 1) |
zf = shift_left (zf, 1); |
rf.h = 0, rf.l = (zf.h >> 22) - 1; |
for (k = 53; k; k--) |
{ |
rf = shift_left (rf, 2); |
xf = shift_left (xf, 1); |
if (k >= 43) |
rf = incr (rf, (zf.h >> (2 * (k - 43))) & 3); |
else if (k >= 27) |
rf = incr (rf, (zf.l >> (2 * (k - 27))) & 3); |
if ((rf.l > xf.l && rf.h >= xf.h) || rf.h > xf.h) |
{ |
xf.l++; |
rf = ominus (rf, xf); |
xf.l++; |
} |
} |
if (rf.h || rf.l) |
xf.l++; |
return fpack (xf, xe, '+', r); |
|
/*:92*/ |
#line 1736 "./mmix-arith.w" |
; |
} |
if (zs == '-') |
x.h |= sign_bit; |
return x; |
} |
|
/*:91*//*93: */ |
#line 1774 "./mmix-arith.w" |
|
octa fremstep ARGS ((octa, octa, int)); |
octa |
fremstep (y, z, delta) |
octa y, z; |
int delta; |
{ |
ftype yt, zt; |
int ye, ze; |
char xs, ys, zs; |
octa x, xf, yf, zf; |
register int xe, thresh, odd; |
yt = funpack (y, &yf, &ye, &ys); |
zt = funpack (z, &zf, &ze, &zs); |
switch (4 * yt + zt) |
{ |
/*42:*/ |
#line 731 "./mmix-arith.w" |
|
case 4 * nan + nan: |
if (!(y.h & 0x80000)) |
exceptions |= I_BIT; |
case 4 * zro + nan: |
case 4 * num + nan: |
case 4 * inf + nan: |
if (!(z.h & 0x80000)) |
exceptions |= I_BIT, z.h |= 0x80000; |
return z; |
case 4 * nan + zro: |
case 4 * nan + num: |
case 4 * nan + inf: |
if (!(y.h & 0x80000)) |
exceptions |= I_BIT, y.h |= 0x80000; |
return y; |
|
/*:42*/ |
#line 1788 "./mmix-arith.w" |
; |
case 4 * zro + zro: |
case 4 * num + zro: |
case 4 * inf + zro: |
case 4 * inf + num: |
case 4 * inf + inf: |
x = standard_NaN; |
exceptions |= I_BIT; |
break; |
case 4 * zro + num: |
case 4 * zro + inf: |
case 4 * num + inf: |
return y; |
case 4 * num + num: /*94: */ |
#line 1809 "./mmix-arith.w" |
|
odd = 0; |
thresh = ye - delta; |
if (thresh < ze) |
thresh = ze; |
while (ye >= thresh) /*95: */ |
#line 1830 "./mmix-arith.w" |
|
{ |
if (yf.h == zf.h && yf.l == zf.l) |
goto zero_out; |
if (yf.h < zf.h || (yf.h == zf.h && yf.l < zf.l)) |
{ |
if (ye == ze) |
goto try_complement; |
ye--, yf = shift_left (yf, 1); |
} |
yf = ominus (yf, zf); |
if (ye == ze) |
odd = 1; |
while (yf.h < 0x400000) |
ye--, yf = shift_left (yf, 1); |
} |
|
/*:95*/ |
#line 1815 "./mmix-arith.w" |
; |
if (ye >= ze) |
{ |
exceptions |= E_BIT; |
return fpack (yf, ye, ys, ROUND_OFF); |
} |
if (ye < ze - 1) |
return fpack (yf, ye, ys, ROUND_OFF); |
yf = shift_right (yf, 1, 1); |
try_complement:xf = ominus (zf, yf), xe = ze, xs = '+' + '-' - ys; |
if (xf.h > yf.h |
|| (xf.h == yf.h && (xf.l > yf.l || (xf.l == yf.l && !odd)))) |
xf = yf, xs = ys; |
while (xf.h < 0x400000) |
xe--, xf = shift_left (xf, 1); |
return fpack (xf, xe, xs, ROUND_OFF); |
|
/*:94*/ |
#line 1793 "./mmix-arith.w" |
; |
zero_out:x = zero_octa; |
} |
if (ys == '-') |
x.h |= sign_bit; |
return x; |
} |
|
/*:93*/ |
#line 41 "./mmix-arith.w" |
|
|
/*:1*/ |
/boilerplate.w
0,0 → 1,27
% This material goes at the beginning of all MMIXware CWEB files |
|
\def\topofcontents{ |
\leftline{\sc\today\ at \hours}\bigskip\bigskip |
\centerline{\titlefont\title}} |
|
\font\ninett=cmtt9 |
\def\botofcontents{\vskip 0pt plus 1filll |
\ninerm\baselineskip10pt |
\noindent\copyright\ 1999 Donald E. Knuth |
\bigskip\noindent |
This file may be freely copied and distributed, provided that |
no changes whatsoever are made. All users are asked to help keep |
the {\ninett MMIX}ware files consistent and ``uncorrupted,'' |
identical everywhere in the world. Changes are permissible only |
if the modified file is given a new name, different from the names of |
existing files in the {\ninett MMIX}ware package, |
and only if the modified file is clearly identified |
as not being part of that package. |
(The {\ninett CWEB} system has a ``change file'' facility by |
which users can easily make minor alterations without modifying |
the master source files in any way. Everybody is supposed to use |
change files instead of changing the files.) |
The author has tried his best to produce correct and useful programs, |
in order to help promote computer science research, |
but no warranty of any kind should be assumed.} |
|
/mmix-arith.tex
0,0 → 1,2631
\input cwebmac |
% This file is part of the MMIXware package (c) Donald E Knuth 1999 |
% This material goes at the beginning of all MMIXware CWEB files |
|
\def\topofcontents{ |
\leftline{\sc\today\ at \hours}\bigskip\bigskip |
\centerline{\titlefont\title}} |
|
\font\ninett=cmtt9 |
\def\botofcontents{\vskip 0pt plus 1filll |
\ninerm\baselineskip10pt |
\noindent\copyright\ 1999 Donald E. Knuth |
\bigskip\noindent |
This file may be freely copied and distributed, provided that |
no changes whatsoever are made. All users are asked to help keep |
the {\ninett MMIX}ware files consistent and ``uncorrupted,'' |
identical everywhere in the world. Changes are permissible only |
if the modified file is given a new name, different from the names of |
existing files in the {\ninett MMIX}ware package, |
and only if the modified file is clearly identified |
as not being part of that package. |
(The {\ninett CWEB} system has a ``change file'' facility by |
which users can easily make minor alterations without modifying |
the master source files in any way. Everybody is supposed to use |
change files instead of changing the files.) |
The author has tried his best to produce correct and useful programs, |
in order to help promote computer science research, |
but no warranty of any kind should be assumed.} |
|
|
\def\title{MMIX-ARITH} |
|
\def\MMIX{\.{MMIX}} |
\def\MMIXAL{\.{MMIXAL}} |
\def\Hex#1{\hbox{$^{\scriptscriptstyle\#}$\tt#1}} % experimental hex constant |
\def\dts{\mathinner{\ldotp\ldotp}} |
\def\<#1>{\hbox{$\langle\,$#1$\,\rangle$}}\let\is=\longrightarrow |
\def\ff{\\{ff\kern-.05em}} |
|
|
|
|
\N{1}{1}Introduction. The subroutines below are used to simulate 64-bit \MMIX\ |
arithmetic on an old-fashioned 32-bit computer---like the one the author |
had when he wrote \MMIXAL\ and the first \MMIX\ simulators in 1998 and 1999. |
All operations are fabricated from 32-bit arithmetic, including |
a full implementation of the IEEE floating point standard, |
assuming only that the \CEE/ compiler has a 32-bit unsigned integer type. |
|
Some day 64-bit machines will be commonplace and the awkward manipulations of |
the present program will look quite archaic. Interested readers who have such |
computers will be able to convert the code to a pure 64-bit form without |
difficulty, thereby obtaining much faster and simpler routines. Meanwhile, |
however, we can simulate the future and hope for continued progress. |
|
This program module has a simple structure, intended to make it |
suitable for loading with \MMIX\ simulators and assemblers. |
|
\Y\B\8\#\&{include} \.{<stdio.h>}\6 |
\8\#\&{include} \.{<string.h>}\6 |
\8\#\&{include} \.{<ctype.h>}\6 |
\X2:Stuff for \CEE/ preprocessor\X\7 |
\&{typedef} \&{enum} ${}\{{}$\5 |
\1${}\\{false},\39{}$\\{true}\5 |
\2${}\}{}$ \&{bool};\7 |
\X3:Tetrabyte and octabyte type definitions\X\6 |
\X36:Other type definitions\X\6 |
\X4:Global variables\X\6 |
\X5:Subroutines\X\par |
\fi |
|
\M{2}Subroutines of this program are declared first with a prototype, |
as in {\mc ANSI C}, then with an old-style \CEE/ function definition. |
Here are some preprocessor commands that make this work correctly with both |
new-style and old-style compilers. |
|
\Y\B\4\X2:Stuff for \CEE/ preprocessor\X${}\E{}$\6 |
\8\#\&{ifdef} \.{\_\_STDC\_\_}\6 |
\8\#\&{define} \.{ARGS}(\\{list}) \5\\{list}\6 |
\8\#\&{else}\6 |
\8\#\&{define} \.{ARGS}(\\{list}) \5(\,)\6 |
\8\#\&{endif}\par |
\U1.\fi |
|
\M{3}The definition of type \&{tetra} should be changed, if necessary, so that |
it represents an unsigned 32-bit integer. |
|
\Y\B\4\X3:Tetrabyte and octabyte type definitions\X${}\E{}$\6 |
\&{typedef} \&{unsigned} \&{int} \&{tetra};\C{ for systems conforming to the |
LP-64 data model }\6 |
\&{typedef} \&{struct} ${}\{{}$\1\6 |
\&{tetra} \|h${},{}$ \|l;\2\6 |
${}\}{}$ \&{octa};\C{ two tetrabytes make one octabyte }\par |
\U1.\fi |
|
\M{4}\B\D$\\{sign\_bit}$ \5 |
((\&{unsigned}) \T{\^80000000})\par |
\Y\B\4\X4:Global variables\X${}\E{}$\6 |
\&{octa} \\{zero\_octa};\C{ \PB{$\\{zero\_octa}.\|h\K\\{zero\_octa}.\|l\K% |
\T{0}$} }\6 |
\&{octa} \\{neg\_one}${}\K\{{-}\T{1},\39{-}\T{1}\}{}$;\C{ \PB{$\\{neg\_one}.\|h% |
\K\\{neg\_one}.\|l\K{-}\T{1}$} }\6 |
\&{octa} \\{inf\_octa}${}\K\{\T{\^7ff00000},\39\T{0}\}{}$;\C{ floating point $+% |
\infty$ }\6 |
\&{octa} \\{standard\_NaN}${}\K\{\T{\^7ff80000},\39\T{0}\}{}$;\C{ floating |
point NaN(.5) }\6 |
\&{octa} \\{aux};\C{ auxiliary output of a subroutine }\6 |
\&{bool} \\{overflow};\C{ set by certain subroutines for signed arithmetic }\par |
\As9, 30, 32, 69\ETs75. |
\U1.\fi |
|
\M{5}It's easy to add and subtract octabytes, if we aren't terribly |
worried about speed. |
|
\Y\B\4\X5:Subroutines\X${}\E{}$\6 |
\&{octa} \\{oplus}\,\,${}\.{ARGS}((\&{octa},\39\&{octa})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{oplus}(\|y,\39\|z{}$)\C{ compute $y+z$ }\1\1\6 |
\&{octa} \|y${},{}$ \|z;\2\2\6 |
${}\{{}$\5 |
\1\&{octa} \|x;\7 |
${}\|x.\|h\K\|y.\|h+\|z.\|h{}$;\5 |
${}\|x.\|l\K\|y.\|l+\|z.\|l;{}$\6 |
\&{if} ${}(\|x.\|l<\|y.\|l){}$\1\5 |
${}\|x.\|h\PP;{}$\2\6 |
\&{return} \|x;\6 |
\4${}\}{}$\2\7 |
\&{octa} \\{ominus}\,\,${}\.{ARGS}((\&{octa},\39\&{octa})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{ominus}(\|y,\39\|z{}$)\C{ compute $y-z$ }\1\1\6 |
\&{octa} \|y${},{}$ \|z;\2\2\6 |
${}\{{}$\5 |
\1\&{octa} \|x;\7 |
${}\|x.\|h\K\|y.\|h-\|z.\|h{}$;\5 |
${}\|x.\|l\K\|y.\|l-\|z.\|l;{}$\6 |
\&{if} ${}(\|x.\|l>\|y.\|l){}$\1\5 |
${}\|x.\|h\MM;{}$\2\6 |
\&{return} \|x;\6 |
\4${}\}{}$\2\par |
\As6, 7, 8, 12, 13, 24, 25, 26, 27, 28, 29, 31, 34, 37, 38, 39, 40, 41, 44, 46, |
50, 54, 60, 61, 62, 68, 82, 85, 86, 88, 89, 91\ETs93. |
\U1.\fi |
|
\M{6}In the following subroutine, \PB{\\{delta}} is a signed quantity that is |
assumed to fit in a signed tetrabyte. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{incr}\,\,${}\.{ARGS}((\&{octa},\39\&{int})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{incr}(\|y,\39\\{delta}{}$)\C{ compute $y+\delta$ }\1% |
\1\6 |
\&{octa} \|y;\6 |
\&{int} \\{delta};\2\2\6 |
${}\{{}$\5 |
\1\&{octa} \|x;\7 |
${}\|x.\|h\K\|y.\|h{}$;\5 |
${}\|x.\|l\K\|y.\|l+\\{delta};{}$\6 |
\&{if} ${}(\\{delta}\G\T{0}){}$\5 |
${}\{{}$\1\6 |
\&{if} ${}(\|x.\|l<\|y.\|l){}$\1\5 |
${}\|x.\|h\PP;{}$\2\6 |
\4${}\}{}$\5 |
\2\&{else} \&{if} ${}(\|x.\|l>\|y.\|l){}$\1\5 |
${}\|x.\|h\MM;{}$\2\6 |
\&{return} \|x;\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{7}Left and right shifts are only a bit more difficult. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{shift\_left}\,\,${}\.{ARGS}((\&{octa},\39\&{int})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{shift\_left}(\|y,\39\|s{}$)\C{ shift left by $s$ |
bits, where $0\le s\le64$ }\1\1\6 |
\&{octa} \|y;\6 |
\&{int} \|s;\2\2\6 |
${}\{{}$\1\6 |
\&{while} ${}(\|s\G\T{32}){}$\1\5 |
${}\|y.\|h\K\|y.\|l,\39\|y.\|l\K\T{0},\39\|s\MRL{-{\K}}\T{32};{}$\2\6 |
\&{if} (\|s)\5 |
${}\{{}$\5 |
\1\&{register} \&{tetra} \\{yhl}${}\K\|y.\|h\LL\|s,{}$ \\{ylh}${}\K\|y.\|l\GG(% |
\T{32}-\|s);{}$\7 |
${}\|y.\|h\K\\{yhl}+\\{ylh}{}$;\5 |
${}\|y.\|l\MRL{{\LL}{\K}}\|s;{}$\6 |
\4${}\}{}$\2\6 |
\&{return} \|y;\6 |
\4${}\}{}$\2\7 |
\&{octa} \\{shift\_right}\,\,${}\.{ARGS}((\&{octa},\39\&{int},\39\&{int})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{shift\_right}(\|y,\39\|s,\39\|u{}$)\C{ shift right, |
arithmetically if $u=0$ }\1\1\6 |
\&{octa} \|y;\6 |
\&{int} \|s${},{}$ \|u;\2\2\6 |
${}\{{}$\1\6 |
\&{while} ${}(\|s\G\T{32}){}$\1\5 |
${}\|y.\|l\K\|y.\|h,\39\|y.\|h\K(\|u\?\T{0}:{-}(\|y.\|h\GG\T{31})),\39\|s% |
\MRL{-{\K}}\T{32};{}$\2\6 |
\&{if} (\|s)\5 |
${}\{{}$\5 |
\1\&{register} \&{tetra} \\{yhl}${}\K\|y.\|h\LL(\T{32}-\|s),{}$ \\{ylh}${}\K% |
\|y.\|l\GG\|s;{}$\7 |
${}\|y.\|h\K(\|u\?\T{0}:({-}(\|y.\|h\GG\T{31}))\LL(\T{32}-\|s))+(\|y.\|h\GG% |
\|s){}$;\5 |
${}\|y.\|l\K\\{yhl}+\\{ylh};{}$\6 |
\4${}\}{}$\2\6 |
\&{return} \|y;\6 |
\4${}\}{}$\2\par |
\fi |
|
\N{1}{8}Multiplication. We need to multiply two unsigned 64-bit integers, |
obtaining |
an unsigned 128-bit product. It is easy to do this on a 32-bit machine |
by using Algorithm 4.3.1M of {\sl Seminumerical Algorithms}, with $b=2^{16}$. |
|
The following subroutine returns the lower half of the product, and |
puts the upper half into a global octabyte called \PB{\\{aux}}. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{omult}\,\,${}\.{ARGS}((\&{octa},\39\&{octa})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{omult}(\|y,\39\|z){}$\1\1\6 |
\&{octa} \|y${},{}$ \|z;\2\2\6 |
${}\{{}$\1\6 |
\&{register} \&{int} \|i${},{}$ \|j${},{}$ \|k;\6 |
\&{tetra} \|u[\T{4}]${},{}$ \|v[\T{4}]${},{}$ \|w[\T{8}];\6 |
\&{register} \&{tetra} \|t;\6 |
\&{octa} \\{acc};\7 |
\X10:Unpack the multiplier and multiplicand to \PB{\|u} and \PB{\|v}\X;\6 |
\&{for} ${}(\|j\K\T{0};{}$ ${}\|j<\T{4};{}$ ${}\|j\PP){}$\1\5 |
${}\|w[\|j]\K\T{0};{}$\2\6 |
\&{for} ${}(\|j\K\T{0};{}$ ${}\|j<\T{4};{}$ ${}\|j\PP){}$\1\6 |
\&{if} ${}(\R\|v[\|j]){}$\1\5 |
${}\|w[\|j+\T{4}]\K\T{0};{}$\2\6 |
\&{else}\5 |
${}\{{}$\1\6 |
\&{for} ${}(\|i\K\|k\K\T{0};{}$ ${}\|i<\T{4};{}$ ${}\|i\PP){}$\5 |
${}\{{}$\1\6 |
${}\|t\K\|u[\|i]*\|v[\|j]+\|w[\|i+\|j]+\|k;{}$\6 |
${}\|w[\|i+\|j]\K\|t\AND\T{\^ffff},\39\|k\K\|t\GG\T{16};{}$\6 |
\4${}\}{}$\2\6 |
${}\|w[\|j+\T{4}]\K\|k;{}$\6 |
\4${}\}{}$\2\2\6 |
\X11:Pack \PB{\|w} into the outputs \PB{\\{aux}} and \PB{\\{acc}}\X;\6 |
\&{return} \\{acc};\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{9}\B\X4:Global variables\X${}\mathrel+\E{}$\6 |
\&{extern} \&{octa} \\{aux};\C{ secondary output of subroutines with multiple |
outputs }\6 |
\&{extern} \&{bool} \\{overflow};\par |
\fi |
|
\M{10}\B\X10:Unpack the multiplier and multiplicand to \PB{\|u} and \PB{\|v}% |
\X${}\E{}$\6 |
$\|u[\T{3}]\K\|y.\|h\GG\T{16},\39\|u[\T{2}]\K\|y.\|h\AND\T{\^ffff},\39\|u[% |
\T{1}]\K\|y.\|l\GG\T{16},\39\|u[\T{0}]\K\|y.\|l\AND\T{\^ffff};{}$\6 |
${}\|v[\T{3}]\K\|z.\|h\GG\T{16},\39\|v[\T{2}]\K\|z.\|h\AND\T{\^ffff},\39\|v[% |
\T{1}]\K\|z.\|l\GG\T{16},\39\|v[\T{0}]\K\|z.\|l\AND\T{\^ffff}{}$;\par |
\U8.\fi |
|
\M{11}\B\X11:Pack \PB{\|w} into the outputs \PB{\\{aux}} and \PB{\\{acc}}\X${}% |
\E{}$\6 |
$\\{aux}.\|h\K(\|w[\T{7}]\LL\T{16})+\|w[\T{6}],\39\\{aux}.\|l\K(\|w[\T{5}]\LL% |
\T{16})+\|w[\T{4}];{}$\6 |
${}\\{acc}.\|h\K(\|w[\T{3}]\LL\T{16})+\|w[\T{2}],\39\\{acc}.\|l\K(\|w[\T{1}]\LL% |
\T{16})+\|w[\T{0}]{}$;\par |
\U8.\fi |
|
\M{12}Signed multiplication has the same lower half product as unsigned |
multiplication. The signed upper half product is obtained with at most two |
further subtractions, after which the result has overflowed if and only if |
the upper half is unequal to 64 copies of the sign bit in the lower half. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{signed\_omult}\,\,${}\.{ARGS}((\&{octa},\39\&{octa})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{signed\_omult}(\|y,\39\|z){}$\1\1\6 |
\&{octa} \|y${},{}$ \|z;\2\2\6 |
${}\{{}$\1\6 |
\&{octa} \\{acc};\7 |
${}\\{acc}\K\\{omult}(\|y,\39\|z);{}$\6 |
\&{if} ${}(\|y.\|h\AND\\{sign\_bit}){}$\1\5 |
${}\\{aux}\K\\{ominus}(\\{aux},\39\|z);{}$\2\6 |
\&{if} ${}(\|z.\|h\AND\\{sign\_bit}){}$\1\5 |
${}\\{aux}\K\\{ominus}(\\{aux},\39\|y);{}$\2\6 |
${}\\{overflow}\K(\\{aux}.\|h\I\\{aux}.\|l\V(\\{aux}.\|h\XOR(\\{aux}.\|h\GG% |
\T{1})\XOR(\\{acc}.\|h\AND\\{sign\_bit})));{}$\6 |
\&{return} \\{acc};\6 |
\4${}\}{}$\2\par |
\fi |
|
\N{1}{13}Division. Long division of an unsigned 128-bit integer by an unsigned |
64-bit integer is, of course, one of the most challenging routines |
needed for \MMIX\ arithmetic. The following program, based on |
Algorithm 4.3.1D of {\sl Seminumerical Algorithms}, computes |
octabytes $q$ and $r$ such that $(2^{64}x+y)=qz+r$ and $0\le r<z$, |
given octabytes $x$, $y$, and~$z$, assuming that $x<z$. |
(If $x\ge z$, it simply sets $q=x$ and $r=y$.) |
The quotient~$q$ is returned by the subroutine; |
the remainder~$r$ is stored in \PB{\\{aux}}. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{odiv}\,\,${}\.{ARGS}((\&{octa},\39\&{octa},\39\&{octa})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{odiv}(\|x,\39\|y,\39\|z){}$\1\1\6 |
\&{octa} \|x${},{}$ \|y${},{}$ \|z;\2\2\6 |
${}\{{}$\1\6 |
\&{register} \&{int} \|i${},{}$ \|j${},{}$ \|k${},{}$ \|n${},{}$ \|d;\6 |
\&{tetra} \|u[\T{8}]${},{}$ \|v[\T{4}]${},{}$ \|q[\T{4}]${},{}$ \\{mask}${},{}$ |
\\{qhat}${},{}$ \\{rhat}${},{}$ \\{vh}${},{}$ \\{vmh};\6 |
\&{register} \&{tetra} \|t;\6 |
\&{octa} \\{acc};\7 |
\X14:Check that \PB{$\|x<\|z$}; otherwise give trivial answer\X;\6 |
\X15:Unpack the dividend and divisor to \PB{\|u} and \PB{\|v}\X;\6 |
\X16:Determine the number of significant places \PB{\|n} in the divisor \PB{% |
\|v}\X;\6 |
\X17:Normalize the divisor\X;\6 |
\&{for} ${}(\|j\K\T{3};{}$ ${}\|j\G\T{0};{}$ ${}\|j\MM){}$\1\5 |
\X20:Determine the quotient digit \PB{\|q[\|j]}\X;\2\6 |
\X18:Unnormalize the remainder\X;\6 |
\X19:Pack \PB{\|q} and \PB{\|u} to \PB{\\{acc}} and \PB{\\{aux}}\X;\6 |
\&{return} \\{acc};\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{14}\B\X14:Check that \PB{$\|x<\|z$}; otherwise give trivial answer\X${}\E{}$% |
\6 |
\&{if} ${}(\|x.\|h>\|z.\|h\V(\|x.\|h\E\|z.\|h\W\|x.\|l\G\|z.\|l)){}$\5 |
${}\{{}$\1\6 |
${}\\{aux}\K\|y{}$;\5 |
\&{return} \|x;\6 |
\4${}\}{}$\2\par |
\U13.\fi |
|
\M{15}\B\X15:Unpack the dividend and divisor to \PB{\|u} and \PB{\|v}\X${}\E{}$% |
\6 |
$\|u[\T{7}]\K\|x.\|h\GG\T{16},\39\|u[\T{6}]\K\|x.\|h\AND\T{\^ffff},\39\|u[% |
\T{5}]\K\|x.\|l\GG\T{16},\39\|u[\T{4}]\K\|x.\|l\AND\T{\^ffff};{}$\6 |
${}\|u[\T{3}]\K\|y.\|h\GG\T{16},\39\|u[\T{2}]\K\|y.\|h\AND\T{\^ffff},\39\|u[% |
\T{1}]\K\|y.\|l\GG\T{16},\39\|u[\T{0}]\K\|y.\|l\AND\T{\^ffff};{}$\6 |
${}\|v[\T{3}]\K\|z.\|h\GG\T{16},\39\|v[\T{2}]\K\|z.\|h\AND\T{\^ffff},\39\|v[% |
\T{1}]\K\|z.\|l\GG\T{16},\39\|v[\T{0}]\K\|z.\|l\AND\T{\^ffff}{}$;\par |
\U13.\fi |
|
\M{16}\B\X16:Determine the number of significant places \PB{\|n} in the divisor |
\PB{\|v}\X${}\E{}$\6 |
\&{for} ${}(\|n\K\T{4};{}$ ${}\|v[\|n-\T{1}]\E\T{0};{}$ ${}\|n\MM){}$\1\5 |
;\2\par |
\U13.\fi |
|
\M{17}We shift \PB{\|u} and \PB{\|v} left by \PB{\|d} places, where \PB{\|d} is |
chosen to |
make $2^{15}\le v_{n-1}<2^{16}$. |
|
\Y\B\4\X17:Normalize the divisor\X${}\E{}$\6 |
$\\{vh}\K\|v[\|n-\T{1}];{}$\6 |
\&{for} ${}(\|d\K\T{0};{}$ ${}\\{vh}<\T{\^8000};{}$ ${}\|d\PP,\39\\{vh}\MRL{{% |
\LL}{\K}}\T{1}){}$\1\5 |
;\2\6 |
\&{for} ${}(\|j\K\|k\K\T{0};{}$ ${}\|j<\|n+\T{4};{}$ ${}\|j\PP){}$\5 |
${}\{{}$\1\6 |
${}\|t\K(\|u[\|j]\LL\|d)+\|k;{}$\6 |
${}\|u[\|j]\K\|t\AND\T{\^ffff},\39\|k\K\|t\GG\T{16};{}$\6 |
\4${}\}{}$\2\6 |
\&{for} ${}(\|j\K\|k\K\T{0};{}$ ${}\|j<\|n;{}$ ${}\|j\PP){}$\5 |
${}\{{}$\1\6 |
${}\|t\K(\|v[\|j]\LL\|d)+\|k;{}$\6 |
${}\|v[\|j]\K\|t\AND\T{\^ffff},\39\|k\K\|t\GG\T{16};{}$\6 |
\4${}\}{}$\2\6 |
${}\\{vh}\K\|v[\|n-\T{1}];{}$\6 |
${}\\{vmh}\K(\|n>\T{1}\?\|v[\|n-\T{2}]:\T{0}){}$;\par |
\U13.\fi |
|
\M{18}\B\X18:Unnormalize the remainder\X${}\E{}$\6 |
$\\{mask}\K(\T{1}\LL\|d)-\T{1};{}$\6 |
\&{for} ${}(\|j\K\T{3};{}$ ${}\|j\G\|n;{}$ ${}\|j\MM){}$\1\5 |
${}\|u[\|j]\K\T{0};{}$\2\6 |
\&{for} ${}(\|k\K\T{0};{}$ ${}\|j\G\T{0};{}$ ${}\|j\MM){}$\5 |
${}\{{}$\1\6 |
${}\|t\K(\|k\LL\T{16})+\|u[\|j];{}$\6 |
${}\|u[\|j]\K\|t\GG\|d,\39\|k\K\|t\AND\\{mask};{}$\6 |
\4${}\}{}$\2\par |
\U13.\fi |
|
\M{19}\B\X19:Pack \PB{\|q} and \PB{\|u} to \PB{\\{acc}} and \PB{\\{aux}}\X${}% |
\E{}$\6 |
$\\{acc}.\|h\K(\|q[\T{3}]\LL\T{16})+\|q[\T{2}],\39\\{acc}.\|l\K(\|q[\T{1}]\LL% |
\T{16})+\|q[\T{0}];{}$\6 |
${}\\{aux}.\|h\K(\|u[\T{3}]\LL\T{16})+\|u[\T{2}],\39\\{aux}.\|l\K(\|u[\T{1}]\LL% |
\T{16})+\|u[\T{0}]{}$;\par |
\U13.\fi |
|
\M{20}\B\X20:Determine the quotient digit \PB{\|q[\|j]}\X${}\E{}$\6 |
${}\{{}$\1\6 |
\X21:Find the trial quotient, $\hat q$\X;\6 |
\X22:Subtract $b^j\hat q v$ from \PB{\|u}\X;\6 |
\X23:If the result was negative, decrease $\hat q$ by 1\X;\6 |
${}\|q[\|j]\K\\{qhat};{}$\6 |
\4${}\}{}$\2\par |
\U13.\fi |
|
\M{21}\B\X21:Find the trial quotient, $\hat q$\X${}\E{}$\6 |
$\|t\K(\|u[\|j+\|n]\LL\T{16})+\|u[\|j+\|n-\T{1}];{}$\6 |
${}\\{qhat}\K\|t/\\{vh},\39\\{rhat}\K\|t-\\{vh}*\\{qhat};{}$\6 |
\&{if} ${}(\|n>\T{1}){}$\1\6 |
\&{while} ${}(\\{qhat}\E\T{\^10000}\V\\{qhat}*\\{vmh}>(\\{rhat}\LL\T{16})+\|u[% |
\|j+\|n-\T{2}]){}$\5 |
${}\{{}$\1\6 |
${}\\{qhat}\MM,\39\\{rhat}\MRL{+{\K}}\\{vh};{}$\6 |
\&{if} ${}(\\{rhat}\G\T{\^10000}){}$\1\5 |
\&{break};\2\6 |
\4${}\}{}$\2\2\par |
\U20.\fi |
|
\M{22}After this step, \PB{$\|u[\|j+\|n]$} will either equal \PB{\|k} or \PB{$% |
\|k-\T{1}$}. The |
true value of~\PB{\|u} would be obtained by subtracting~\PB{\|k} from \PB{$\|u[% |
\|j+\|n]$}; |
but we don't have to fuss over \PB{$\|u[\|j+\|n]$}, because it won't be |
examined later. |
|
\Y\B\4\X22:Subtract $b^j\hat q v$ from \PB{\|u}\X${}\E{}$\6 |
\&{for} ${}(\|i\K\|k\K\T{0};{}$ ${}\|i<\|n;{}$ ${}\|i\PP){}$\5 |
${}\{{}$\1\6 |
${}\|t\K\|u[\|i+\|j]+\T{\^ffff0000}-\|k-\\{qhat}*\|v[\|i];{}$\6 |
${}\|u[\|i+\|j]\K\|t\AND\T{\^ffff},\39\|k\K\T{\^ffff}-(\|t\GG\T{16});{}$\6 |
\4${}\}{}$\2\par |
\U20.\fi |
|
\M{23}The correction here occurs only rarely, but it can be necessary---for |
example, when dividing the number \Hex{7fff800100000000} by \Hex{800080020005}. |
|
\Y\B\4\X23:If the result was negative, decrease $\hat q$ by 1\X${}\E{}$\6 |
\&{if} ${}(\|u[\|j+\|n]\I\|k){}$\5 |
${}\{{}$\1\6 |
${}\\{qhat}\MM;{}$\6 |
\&{for} ${}(\|i\K\|k\K\T{0};{}$ ${}\|i<\|n;{}$ ${}\|i\PP){}$\5 |
${}\{{}$\1\6 |
${}\|t\K\|u[\|i+\|j]+\|v[\|i]+\|k;{}$\6 |
${}\|u[\|i+\|j]\K\|t\AND\T{\^ffff},\39\|k\K\|t\GG\T{16};{}$\6 |
\4${}\}{}$\2\6 |
\4${}\}{}$\2\par |
\U20.\fi |
|
\M{24}Signed division can be reduced to unsigned division in a tedious |
but straightforward manner. We assume that the divisor isn't zero. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{signed\_odiv}\,\,${}\.{ARGS}((\&{octa},\39\&{octa})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{signed\_odiv}(\|y,\39\|z){}$\1\1\6 |
\&{octa} \|y${},{}$ \|z;\2\2\6 |
${}\{{}$\1\6 |
\&{octa} \\{yy}${},{}$ \\{zz}${},{}$ \|q;\6 |
\&{register} \&{int} \\{sy}${},{}$ \\{sz};\7 |
\&{if} ${}(\|y.\|h\AND\\{sign\_bit}){}$\1\5 |
${}\\{sy}\K\T{2},\39\\{yy}\K\\{ominus}(\\{zero\_octa},\39\|y);{}$\2\6 |
\&{else}\1\5 |
${}\\{sy}\K\T{0},\39\\{yy}\K\|y;{}$\2\6 |
\&{if} ${}(\|z.\|h\AND\\{sign\_bit}){}$\1\5 |
${}\\{sz}\K\T{1},\39\\{zz}\K\\{ominus}(\\{zero\_octa},\39\|z);{}$\2\6 |
\&{else}\1\5 |
${}\\{sz}\K\T{0},\39\\{zz}\K\|z;{}$\2\6 |
${}\|q\K\\{odiv}(\\{zero\_octa},\39\\{yy},\39\\{zz});{}$\6 |
${}\\{overflow}\K\\{false};{}$\6 |
\&{switch} ${}(\\{sy}+\\{sz}){}$\5 |
${}\{{}$\1\6 |
\4\&{case} \T{2}${}+\T{1}{}$:\5 |
${}\\{aux}\K\\{ominus}(\\{zero\_octa},\39\\{aux});{}$\6 |
\&{if} ${}(\|q.\|h\E\\{sign\_bit}){}$\1\5 |
${}\\{overflow}\K\\{true};{}$\2\6 |
\4\&{case} \T{0}${}+\T{0}{}$:\5 |
\&{return} \|q;\6 |
\4\&{case} \T{2}${}+\T{0}{}$:\5 |
\&{if} ${}(\\{aux}.\|h\V\\{aux}.\|l){}$\1\5 |
${}\\{aux}\K\\{ominus}(\\{zz},\39\\{aux});{}$\2\6 |
\&{goto} \\{negate\_q};\6 |
\4\&{case} \T{0}${}+\T{1}{}$:\5 |
\&{if} ${}(\\{aux}.\|h\V\\{aux}.\|l){}$\1\5 |
${}\\{aux}\K\\{ominus}(\\{aux},\39\\{zz});{}$\2\6 |
\4\\{negate\_q}:\5 |
\&{if} ${}(\\{aux}.\|h\V\\{aux}.\|l){}$\1\5 |
\&{return} \\{ominus}${}(\\{neg\_one},\39\|q);{}$\2\6 |
\&{else}\1\5 |
\&{return} \\{ominus}${}(\\{zero\_octa},\39\|q);{}$\2\6 |
\4${}\}{}$\2\6 |
\4${}\}{}$\2\par |
\fi |
|
\N{1}{25}Bit fiddling. The bitwise operators of \MMIX\ are fairly easy to |
implement directly, but three of them occur often enough to deserve |
packaging as subroutines. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{oand}\,\,${}\.{ARGS}((\&{octa},\39\&{octa})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{oand}(\|y,\39\|z{}$)\C{ compute $y\land z$ }\1\1\6 |
\&{octa} \|y${},{}$ \|z;\2\2\6 |
${}\{{}$\5 |
\1\&{octa} \|x;\7 |
${}\|x.\|h\K\|y.\|h\AND\|z.\|h{}$;\5 |
${}\|x.\|l\K\|y.\|l\AND\|z.\|l;{}$\6 |
\&{return} \|x;\6 |
\4${}\}{}$\2\7 |
\&{octa} \\{oandn}\,\,${}\.{ARGS}((\&{octa},\39\&{octa})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{oandn}(\|y,\39\|z{}$)\C{ compute $y\land\bar z$ }\1\1% |
\6 |
\&{octa} \|y${},{}$ \|z;\2\2\6 |
${}\{{}$\5 |
\1\&{octa} \|x;\7 |
${}\|x.\|h\K\|y.\|h\AND\CM\|z.\|h{}$;\5 |
${}\|x.\|l\K\|y.\|l\AND\CM\|z.\|l;{}$\6 |
\&{return} \|x;\6 |
\4${}\}{}$\2\7 |
\&{octa} \\{oxor}\,\,${}\.{ARGS}((\&{octa},\39\&{octa})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{oxor}(\|y,\39\|z{}$)\C{ compute $y\oplus z$ }\1\1\6 |
\&{octa} \|y${},{}$ \|z;\2\2\6 |
${}\{{}$\5 |
\1\&{octa} \|x;\7 |
${}\|x.\|h\K\|y.\|h\XOR\|z.\|h{}$;\5 |
${}\|x.\|l\K\|y.\|l\XOR\|z.\|l;{}$\6 |
\&{return} \|x;\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{26}Here's a fun way to count the number of bits in a tetrabyte. |
[This classical trick is called the ``Gillies--Miller method |
for sideways addition'' in {\sl The Preparation of Programs |
for an Electronic Digital Computer\/} by Wilkes, Wheeler, and |
Gill, second edition (Reading, Mass.:\ Addison--Wesley, 1957), |
191--193. Some of the tricks used here were suggested by |
Balbir Singh, Peter Rossmanith, and Stefan Schwoon.] |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{int} \\{count\_bits}\,\,\.{ARGS}((\&{tetra}));\5 |
\hbox{}\6{}\&{int} \\{count\_bits}(\|x)\1\1\6 |
\&{tetra} \|x;\2\2\6 |
${}\{{}$\1\6 |
\&{register} \&{int} \\{xx}${}\K\|x;{}$\7 |
${}\\{xx}\K\\{xx}-((\\{xx}\GG\T{1})\AND\T{\^55555555});{}$\6 |
${}\\{xx}\K(\\{xx}\AND\T{\^33333333})+((\\{xx}\GG\T{2})\AND\T{\^33333333});{}$\6 |
${}\\{xx}\K(\\{xx}+(\\{xx}\GG\T{4}))\AND\T{\^0f0f0f0f};{}$\6 |
${}\\{xx}\K\\{xx}+(\\{xx}\GG\T{8});{}$\6 |
\&{return} ${}(\\{xx}+(\\{xx}\GG\T{16}))\AND\T{\^ff};{}$\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{27}To compute the nonnegative byte differences of two given tetrabytes, |
we can carry out the following 20-step branchless computation: |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{tetra} \\{byte\_diff}\,\,${}\.{ARGS}((\&{tetra},\39\&{tetra})){}$;\5 |
\hbox{}\6{}\&{tetra} ${}\\{byte\_diff}(\|y,\39\|z){}$\1\1\6 |
\&{tetra} \|y${},{}$ \|z;\2\2\6 |
${}\{{}$\1\6 |
\&{register} \&{tetra} \|d${}\K(\|y\AND\T{\^00ff00ff})+\T{\^01000100}-(\|z\AND% |
\T{\^00ff00ff});{}$\6 |
\&{register} \&{tetra} \|m${}\K\|d\AND\T{\^01000100};{}$\6 |
\&{register} \&{tetra} \|x${}\K\|d\AND(\|m-(\|m\GG\T{8}));{}$\7 |
${}\|d\K((\|y\GG\T{8})\AND\T{\^00ff00ff})+\T{\^01000100}-((\|z\GG\T{8})\AND\T{% |
\^00ff00ff});{}$\6 |
${}\|m\K\|d\AND\T{\^01000100};{}$\6 |
\&{return} \|x${}+((\|d\AND(\|m-(\|m\GG\T{8})))\LL\T{8});{}$\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{28}To compute the nonnegative wyde differences of two tetrabytes, |
another trick leads to a 15-step branchless computation. |
(Research problem: Can \PB{\\{count\_bits}}, \PB{\\{byte\_diff}}, or \PB{% |
\\{wyde\_diff}} be done |
with fewer operations?) |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{tetra} \\{wyde\_diff}\,\,${}\.{ARGS}((\&{tetra},\39\&{tetra})){}$;\5 |
\hbox{}\6{}\&{tetra} ${}\\{wyde\_diff}(\|y,\39\|z){}$\1\1\6 |
\&{tetra} \|y${},{}$ \|z;\2\2\6 |
${}\{{}$\1\6 |
\&{register} \&{tetra} \|a${}\K((\|y\GG\T{16})-(\|z\GG\T{16}))\AND\T{% |
\^10000};{}$\6 |
\&{register} \&{tetra} \|b${}\K((\|y\AND\T{\^ffff})-(\|z\AND\T{\^ffff}))\AND\T{% |
\^10000};{}$\7 |
\&{return} \|y${}-(\|z\XOR((\|y\XOR\|z)\AND(\|b-\|a-(\|b\GG\T{16}))));{}$\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{29}The last bitwise subroutine we need is the most interesting: |
It implements \MMIX's \.{MOR} and \.{MXOR} operations. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{bool\_mult}\,\,${}\.{ARGS}((\&{octa},\39\&{octa},\39\&{bool})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{bool\_mult}(\|y,\39\|z,\39\\{xor}){}$\1\1\6 |
\&{octa} \|y${},{}$ \|z;\C{ the operands }\6 |
\&{bool} \\{xor};\C{ do we do xor instead of or? }\2\2\6 |
${}\{{}$\1\6 |
\&{octa} \|o${},{}$ \|x;\6 |
\&{register} \&{tetra} \|a${},{}$ \|b${},{}$ \|c;\6 |
\&{register} \&{int} \|k;\7 |
\&{for} ${}(\|k\K\T{0},\39\|o\K\|y,\39\|x\K\\{zero\_octa};{}$ ${}\|o.\|h\V\|o.% |
\|l;{}$ ${}\|k\PP,\39\|o\K\\{shift\_right}(\|o,\39\T{8},\39\T{1})){}$\1\6 |
\&{if} ${}(\|o.\|l\AND\T{\^ff}){}$\5 |
${}\{{}$\1\6 |
${}\|a\K((\|z.\|h\GG\|k)\AND\T{\^01010101})*\T{\^ff};{}$\6 |
${}\|b\K((\|z.\|l\GG\|k)\AND\T{\^01010101})*\T{\^ff};{}$\6 |
${}\|c\K(\|o.\|l\AND\T{\^ff})*\T{\^01010101};{}$\6 |
\&{if} (\\{xor})\1\5 |
${}\|x.\|h\MRL{{\XOR}{\K}}\|a\AND\|c,\39\|x.\|l\MRL{{\XOR}{\K}}\|b\AND\|c;{}$\2% |
\6 |
\&{else}\1\5 |
${}\|x.\|h\MRL{{\OR}{\K}}\|a\AND\|c,\39\|x.\|l\MRL{{\OR}{\K}}\|b\AND\|c;{}$\2\6 |
\4${}\}{}$\2\2\6 |
\&{return} \|x;\6 |
\4${}\}{}$\2\par |
\fi |
|
\N{1}{30}Floating point packing and unpacking. Standard IEEE floating binary |
numbers pack a sign, exponent, and fraction into a tetrabyte |
or octabyte. In this section we consider basic subroutines that |
convert between IEEE format and the separate unpacked components. |
|
\Y\B\4\D$\.{ROUND\_OFF}$ \5 |
\T{1}\par |
\B\4\D$\.{ROUND\_UP}$ \5 |
\T{2}\par |
\B\4\D$\.{ROUND\_DOWN}$ \5 |
\T{3}\par |
\B\4\D$\.{ROUND\_NEAR}$ \5 |
\T{4}\par |
\Y\B\4\X4:Global variables\X${}\mathrel+\E{}$\6 |
\&{int} \\{cur\_round};\C{ the current rounding mode }\par |
\fi |
|
\M{31}The \PB{\\{fpack}} routine takes an octabyte $f$, a raw exponent~$e$, |
and a sign~\PB{\|s}, and packs them |
into the floating binary number that corresponds to |
$\pm2^{e-1076}f$, using a given rounding mode. |
The value of $f$ should satisfy $2^{54}\le f\le 2^{55}$. |
|
Thus, for example, the floating binary number $+1.0=\Hex{3ff0000000000000}$ |
is obtained when $f=2^{54}$, $e=\Hex{3fe}$, and \PB{$\|s\K\.{'+'}$}. |
The raw exponent~$e$ is usually one less than |
the final exponent value; the leading bit of~$f$ is essentially added |
to the exponent. (This trick works nicely for subnormal numbers, when |
$e<0$, or in cases where the value of $f$ is rounded upwards to $2^{55}$.) |
|
Exceptional events are noted by oring appropriate bits into |
the global variable \PB{\\{exceptions}}. Special considerations apply to |
underflow, which is not fully specified by Section 7.4 of the IEEE standard: |
Implementations of the standard are free to choose between two definitions |
of ``tininess'' and two definitions of ``accuracy loss.'' |
\MMIX\ determines tininess {\it after\/} rounding, hence a result with |
$e<0$ is not necessarily tiny; \MMIX\ treats accuracy loss as equivalent |
to inexactness. Thus, a result underflows if and only if |
it is tiny and either (i)~it is inexact or (ii)~the underflow trap is enabled. |
The \PB{\\{fpack}} routine sets \PB{\.{U\_BIT}} in \PB{\\{exceptions}} if and |
only if the result is |
tiny, \PB{\.{X\_BIT}} if and only if the result is inexact. |
|
\Y\B\4\D$\.{X\_BIT}$ \5 |
$(\T{1}\LL\T{8}{}$)\C{ floating inexact }\par |
\B\4\D$\.{Z\_BIT}$ \5 |
$(\T{1}\LL\T{9}{}$)\C{ floating division by zero }\par |
\B\4\D$\.{U\_BIT}$ \5 |
$(\T{1}\LL\T{10}{}$)\C{ floating underflow }\par |
\B\4\D$\.{O\_BIT}$ \5 |
$(\T{1}\LL\T{11}{}$)\C{ floating overflow }\par |
\B\4\D$\.{I\_BIT}$ \5 |
$(\T{1}\LL\T{12}{}$)\C{ floating invalid operation }\par |
\B\4\D$\.{W\_BIT}$ \5 |
$(\T{1}\LL\T{13}{}$)\C{ float-to-fix overflow }\par |
\B\4\D$\.{V\_BIT}$ \5 |
$(\T{1}\LL\T{14}{}$)\C{ integer overflow }\par |
\B\4\D$\.{D\_BIT}$ \5 |
$(\T{1}\LL\T{15}{}$)\C{ integer divide check }\par |
\B\4\D$\.{E\_BIT}$ \5 |
$(\T{1}\LL\T{18}{}$)\C{ external (dynamic) trap bit }\par |
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{fpack}\,\,${}\.{ARGS}((\&{octa},\39\&{int},\39\&{char},\39% |
\&{int})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{fpack}(\|f,\39\|e,\39\|s,\39\|r){}$\1\1\6 |
\&{octa} \|f;\C{ the normalized fraction part }\6 |
\&{int} \|e;\C{ the raw exponent }\6 |
\&{char} \|s;\C{ the sign }\6 |
\&{int} \|r;\C{ the rounding mode }\2\2\6 |
${}\{{}$\1\6 |
\&{octa} \|o;\7 |
\&{if} ${}(\|e>\T{\^7fd}){}$\1\5 |
${}\|e\K\T{\^7ff},\39\|o\K\\{zero\_octa};{}$\2\6 |
\&{else}\5 |
${}\{{}$\1\6 |
\&{if} ${}(\|e<\T{0}){}$\5 |
${}\{{}$\1\6 |
\&{if} ${}(\|e<{-}\T{54}){}$\1\5 |
${}\|o.\|h\K\T{0},\39\|o.\|l\K\T{1};{}$\2\6 |
\&{else}\5 |
${}\{{}$\5 |
\1\&{octa} \\{oo};\7 |
${}\|o\K\\{shift\_right}(\|f,\39{-}\|e,\39\T{1});{}$\6 |
${}\\{oo}\K\\{shift\_left}(\|o,\39{-}\|e);{}$\6 |
\&{if} ${}(\\{oo}.\|l\I\|f.\|l\V\\{oo}.\|h\I\|f.\|h){}$\1\5 |
${}\|o.\|l\MRL{{\OR}{\K}}\T{1}{}$;\C{ sticky bit }\2\6 |
\4${}\}{}$\2\6 |
${}\|e\K\T{0};{}$\6 |
\4${}\}{}$\5 |
\2\&{else}\1\5 |
${}\|o\K\|f;{}$\2\6 |
\4${}\}{}$\2\6 |
\X33:Round and return the result\X;\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{32}\B\X4:Global variables\X${}\mathrel+\E{}$\6 |
\&{int} \\{exceptions};\C{ bits possibly destined for rA }\par |
\fi |
|
\M{33}Everything falls together so nicely here, it's almost too good to be |
true! |
|
\Y\B\4\X33:Round and return the result\X${}\E{}$\6 |
\&{if} ${}(\|o.\|l\AND\T{3}){}$\1\5 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{X\_BIT};{}$\2\6 |
\&{switch} (\|r)\5 |
${}\{{}$\1\6 |
\4\&{case} \.{ROUND\_DOWN}:\5 |
\&{if} ${}(\|s\E\.{'-'}){}$\1\5 |
${}\|o\K\\{incr}(\|o,\39\T{3}){}$;\5 |
\2\&{break};\6 |
\4\&{case} \.{ROUND\_UP}:\5 |
\&{if} ${}(\|s\I\.{'-'}){}$\1\5 |
${}\|o\K\\{incr}(\|o,\39\T{3});{}$\2\6 |
\4\&{case} \.{ROUND\_OFF}:\5 |
\&{break};\6 |
\4\&{case} \.{ROUND\_NEAR}:\5 |
${}\|o\K\\{incr}(\|o,\39\|o.\|l\AND\T{4}\?\T{2}:\T{1}){}$;\5 |
\&{break};\6 |
\4${}\}{}$\2\6 |
${}\|o\K\\{shift\_right}(\|o,\39\T{2},\39\T{1});{}$\6 |
${}\|o.\|h\MRL{+{\K}}\|e\LL\T{20};{}$\6 |
\&{if} ${}(\|o.\|h\G\T{\^7ff00000}){}$\1\5 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{O\_BIT}+\.{X\_BIT}{}$;\C{ overflow }\2\6 |
\&{else} \&{if} ${}(\|o.\|h<\T{\^100000}){}$\1\5 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{U\_BIT}{}$;\C{ tininess }\2\6 |
\&{if} ${}(\|s\E\.{'-'}){}$\1\5 |
${}\|o.\|h\MRL{{\OR}{\K}}\\{sign\_bit};{}$\2\6 |
\&{return} \|o;\par |
\U31.\fi |
|
\M{34}Similarly, \PB{\\{sfpack}} packs a short float, from inputs |
having the same conventions as \PB{\\{fpack}}. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{tetra} \\{sfpack}\,\,${}\.{ARGS}((\&{octa},\39\&{int},\39\&{char},\39% |
\&{int})){}$;\5 |
\hbox{}\6{}\&{tetra} ${}\\{sfpack}(\|f,\39\|e,\39\|s,\39\|r){}$\1\1\6 |
\&{octa} \|f;\C{ the fraction part }\6 |
\&{int} \|e;\C{ the raw exponent }\6 |
\&{char} \|s;\C{ the sign }\6 |
\&{int} \|r;\C{ the rounding mode }\2\2\6 |
${}\{{}$\1\6 |
\&{register} \&{tetra} \|o;\7 |
\&{if} ${}(\|e>\T{\^47d}){}$\1\5 |
${}\|e\K\T{\^47f},\39\|o\K\T{0};{}$\2\6 |
\&{else}\5 |
${}\{{}$\1\6 |
${}\|o\K\\{shift\_left}(\|f,\39\T{3}).\|h;{}$\6 |
\&{if} ${}(\|f.\|l\AND\T{\^1fffffff}){}$\1\5 |
${}\|o\MRL{{\OR}{\K}}\T{1};{}$\2\6 |
\&{if} ${}(\|e<\T{\^380}){}$\5 |
${}\{{}$\1\6 |
\&{if} ${}(\|e<\T{\^380}-\T{25}){}$\1\5 |
${}\|o\K\T{1};{}$\2\6 |
\&{else}\5 |
${}\{{}$\5 |
\1\&{register} \&{tetra} \\{o0}${},{}$ \\{oo};\7 |
${}\\{o0}\K\|o;{}$\6 |
${}\|o\K\|o\GG(\T{\^380}-\|e);{}$\6 |
${}\\{oo}\K\|o\LL(\T{\^380}-\|e);{}$\6 |
\&{if} ${}(\\{oo}\I\\{o0}){}$\1\5 |
${}\|o\MRL{{\OR}{\K}}\T{1}{}$;\C{ sticky bit }\2\6 |
\4${}\}{}$\2\6 |
${}\|e\K\T{\^380};{}$\6 |
\4${}\}{}$\2\6 |
\4${}\}{}$\2\6 |
\X35:Round and return the short result\X;\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{35}\B\X35:Round and return the short result\X${}\E{}$\6 |
\&{if} ${}(\|o\AND\T{3}){}$\1\5 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{X\_BIT};{}$\2\6 |
\&{switch} (\|r)\5 |
${}\{{}$\1\6 |
\4\&{case} \.{ROUND\_DOWN}:\5 |
\&{if} ${}(\|s\E\.{'-'}){}$\1\5 |
${}\|o\MRL{+{\K}}\T{3}{}$;\5 |
\2\&{break};\6 |
\4\&{case} \.{ROUND\_UP}:\5 |
\&{if} ${}(\|s\I\.{'-'}){}$\1\5 |
${}\|o\MRL{+{\K}}\T{3};{}$\2\6 |
\4\&{case} \.{ROUND\_OFF}:\5 |
\&{break};\6 |
\4\&{case} \.{ROUND\_NEAR}:\5 |
${}\|o\MRL{+{\K}}(\|o\AND\T{4}\?\T{2}:\T{1}){}$;\5 |
\&{break};\6 |
\4${}\}{}$\2\6 |
${}\|o\K\|o\GG\T{2};{}$\6 |
${}\|o\MRL{+{\K}}(\|e-\T{\^380})\LL\T{23};{}$\6 |
\&{if} ${}(\|o\G\T{\^7f800000}){}$\1\5 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{O\_BIT}+\.{X\_BIT}{}$;\C{ overflow }\2\6 |
\&{else} \&{if} ${}(\|o<\T{\^100000}){}$\1\5 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{U\_BIT}{}$;\C{ tininess }\2\6 |
\&{if} ${}(\|s\E\.{'-'}){}$\1\5 |
${}\|o\MRL{{\OR}{\K}}\\{sign\_bit};{}$\2\6 |
\&{return} \|o;\par |
\U34.\fi |
|
\M{36}The \PB{\\{funpack}} routine is, roughly speaking, the opposite of \PB{% |
\\{fpack}}. |
It takes a given floating point number~$x$ and separates out its |
fraction part~$f$, exponent~$e$, and sign~$s$. It clears \PB{\\{exceptions}} |
to zero. It returns the type of value found: \PB{\\{zro}}, \PB{\\{num}}, \PB{% |
\\{inf}}, |
or \PB{\\{nan}}. When it returns \PB{\\{num}}, |
it will have set $f$, $e$, and~$s$ |
to the values from which \PB{\\{fpack}} would produce the original number~$x$ |
without exceptions. |
|
\Y\B\4\D$\\{zero\_exponent}$ \5 |
$({-}\T{1000}{}$)\C{ zero is assumed to have this exponent }\par |
\Y\B\4\X36:Other type definitions\X${}\E{}$\6 |
\&{typedef} \&{enum} ${}\{{}$\1\6 |
${}\\{zro},\39\\{num},\39\\{inf},\39\\{nan}{}$\2\6 |
${}\}{}$\5 |
\&{ftype};\par |
\A59. |
\U1.\fi |
|
\M{37}\B\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{ftype} \\{funpack}\,\,${}\.{ARGS}((\&{octa},\39{}$\&{octa} ${}{*},\39{}$% |
\&{int} ${}{*},\39{}$\&{char} ${}{*})){}$;\5 |
\hbox{}\6{}\&{ftype} ${}\\{funpack}(\|x,\39\|f,\39\|e,\39\|s){}$\1\1\6 |
\&{octa} \|x;\C{ the given floating point value }\6 |
\&{octa} ${}{*}\|f{}$;\C{ address where the fraction part should be stored }\6 |
\&{int} ${}{*}\|e{}$;\C{ address where the exponent part should be stored }\6 |
\&{char} ${}{*}\|s{}$;\C{ address where the sign should be stored }\2\2\6 |
${}\{{}$\1\6 |
\&{register} \&{int} \\{ee};\7 |
${}\\{exceptions}\K\T{0};{}$\6 |
${}{*}\|s\K(\|x.\|h\AND\\{sign\_bit}\?\.{'-'}:\.{'+'});{}$\6 |
${}{*}\|f\K\\{shift\_left}(\|x,\39\T{2});{}$\6 |
${}\|f\MG\|h\MRL{\AND{\K}}\T{\^3fffff};{}$\6 |
${}\\{ee}\K(\|x.\|h\GG\T{20})\AND\T{\^7ff};{}$\6 |
\&{if} (\\{ee})\5 |
${}\{{}$\1\6 |
${}{*}\|e\K\\{ee}-\T{1};{}$\6 |
${}\|f\MG\|h\MRL{{\OR}{\K}}\T{\^400000};{}$\6 |
\&{return} ${}(\\{ee}<\T{\^7ff}\?\\{num}:\|f\MG\|h\E\T{\^400000}\W\R\|f\MG\|l\?% |
\\{inf}:\\{nan});{}$\6 |
\4${}\}{}$\2\6 |
\&{if} ${}(\R\|x.\|l\W\R\|f\MG\|h){}$\5 |
${}\{{}$\1\6 |
${}{*}\|e\K\\{zero\_exponent}{}$;\5 |
\&{return} \\{zro};\6 |
\4${}\}{}$\2\6 |
\&{do}\5 |
${}\{{}$\5 |
\1${}\\{ee}\MM{}$;\5 |
${}{*}\|f\K\\{shift\_left}({*}\|f,\39\T{1}){}$;\5 |
${}\}{}$\2\5 |
\&{while} ${}(\R(\|f\MG\|h\AND\T{\^400000}));{}$\6 |
${}{*}\|e\K\\{ee}{}$;\5 |
\&{return} \\{num};\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{38}\B\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{ftype} \\{sfunpack}\,\,${}\.{ARGS}((\&{tetra},\39{}$\&{octa} ${}{*},\39{}$% |
\&{int} ${}{*},\39{}$\&{char} ${}{*})){}$;\5 |
\hbox{}\6{}\&{ftype} ${}\\{sfunpack}(\|x,\39\|f,\39\|e,\39\|s){}$\1\1\6 |
\&{tetra} \|x;\C{ the given floating point value }\6 |
\&{octa} ${}{*}\|f{}$;\C{ address where the fraction part should be stored }\6 |
\&{int} ${}{*}\|e{}$;\C{ address where the exponent part should be stored }\6 |
\&{char} ${}{*}\|s{}$;\C{ address where the sign should be stored }\2\2\6 |
${}\{{}$\1\6 |
\&{register} \&{int} \\{ee};\7 |
${}\\{exceptions}\K\T{0};{}$\6 |
${}{*}\|s\K(\|x\AND\\{sign\_bit}\?\.{'-'}:\.{'+'});{}$\6 |
${}\|f\MG\|h\K(\|x\GG\T{1})\AND\T{\^3fffff},\39\|f\MG\|l\K\|x\LL\T{31};{}$\6 |
${}\\{ee}\K(\|x\GG\T{23})\AND\T{\^ff};{}$\6 |
\&{if} (\\{ee})\5 |
${}\{{}$\1\6 |
${}{*}\|e\K\\{ee}+\T{\^380}-\T{1};{}$\6 |
${}\|f\MG\|h\MRL{{\OR}{\K}}\T{\^400000};{}$\6 |
\&{return} ${}(\\{ee}<\T{\^ff}\?\\{num}:(\|x\AND\T{\^7fffffff})\E\T{\^7f800000}% |
\?\\{inf}:\\{nan});{}$\6 |
\4${}\}{}$\2\6 |
\&{if} ${}(\R(\|x\AND\T{\^7fffffff})){}$\5 |
${}\{{}$\1\6 |
${}{*}\|e\K\\{zero\_exponent}{}$;\5 |
\&{return} \\{zro};\6 |
\4${}\}{}$\2\6 |
\&{do}\5 |
${}\{{}$\5 |
\1${}\\{ee}\MM{}$;\5 |
${}{*}\|f\K\\{shift\_left}({*}\|f,\39\T{1}){}$;\5 |
${}\}{}$\2\5 |
\&{while} ${}(\R(\|f\MG\|h\AND\T{\^400000}));{}$\6 |
${}{*}\|e\K\\{ee}+\T{\^380}{}$;\5 |
\&{return} \\{num};\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{39}Since \MMIX\ downplays 32-bit operations, it uses \PB{\\{sfpack}} and % |
\PB{\\{sfunpack}} |
only when loading and storing short floats, or when converting |
from fixed point to floating point. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{load\_sf}\,\,\.{ARGS}((\&{tetra}));\5 |
\hbox{}\6{}\&{octa} \\{load\_sf}(\|z)\1\1\6 |
\&{tetra} \|z;\C{ 32 bits to be loaded into a 64-bit register }\2\2\6 |
${}\{{}$\1\6 |
\&{octa} \|f${},{}$ \|x;\5 |
\&{int} \|e;\5 |
\&{char} \|s;\5 |
\&{ftype} \|t;\7 |
${}\|t\K\\{sfunpack}(\|z,\39{\AND}\|f,\39{\AND}\|e,\39{\AND}\|s);{}$\6 |
\&{switch} (\|t)\5 |
${}\{{}$\1\6 |
\4\&{case} \\{zro}:\5 |
${}\|x\K\\{zero\_octa}{}$;\5 |
\&{break};\6 |
\4\&{case} \\{num}:\5 |
\&{return} \\{fpack}${}(\|f,\39\|e,\39\|s,\39\.{ROUND\_OFF});{}$\6 |
\4\&{case} \\{inf}:\5 |
${}\|x\K\\{inf\_octa}{}$;\5 |
\&{break};\6 |
\4\&{case} \\{nan}:\5 |
${}\|x\K\\{shift\_right}(\|f,\39\T{2},\39\T{1}){}$;\5 |
${}\|x.\|h\MRL{{\OR}{\K}}\T{\^7ff00000}{}$;\5 |
\&{break};\6 |
\4${}\}{}$\2\6 |
\&{if} ${}(\|s\E\.{'-'}){}$\1\5 |
${}\|x.\|h\MRL{{\OR}{\K}}\\{sign\_bit};{}$\2\6 |
\&{return} \|x;\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{40}\B\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{tetra} \\{store\_sf}\,\,\.{ARGS}((\&{octa}));\5 |
\hbox{}\6{}\&{tetra} \\{store\_sf}(\|x)\1\1\6 |
\&{octa} \|x;\C{ 64 bits to be loaded into a 32-bit word }\2\2\6 |
${}\{{}$\1\6 |
\&{octa} \|f;\5 |
\&{tetra} \|z;\5 |
\&{int} \|e;\5 |
\&{char} \|s;\5 |
\&{ftype} \|t;\7 |
${}\|t\K\\{funpack}(\|x,\39{\AND}\|f,\39{\AND}\|e,\39{\AND}\|s);{}$\6 |
\&{switch} (\|t)\5 |
${}\{{}$\1\6 |
\4\&{case} \\{zro}:\5 |
${}\|z\K\T{0}{}$;\5 |
\&{break};\6 |
\4\&{case} \\{num}:\5 |
\&{return} \\{sfpack}${}(\|f,\39\|e,\39\|s,\39\\{cur\_round});{}$\6 |
\4\&{case} \\{inf}:\5 |
${}\|z\K\T{\^7f800000}{}$;\5 |
\&{break};\6 |
\4\&{case} \\{nan}:\5 |
\&{if} ${}(\R(\|f.\|h\AND\T{\^200000})){}$\5 |
${}\{{}$\1\6 |
${}\|f.\|h\MRL{{\OR}{\K}}\T{\^200000}{}$;\5 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{I\_BIT}{}$;\C{ NaN was signaling }\6 |
\4${}\}{}$\2\6 |
${}\|z\K\T{\^7f800000}\OR(\|f.\|h\LL\T{1})\OR(\|f.\|l\GG\T{31}){}$;\5 |
\&{break};\6 |
\4${}\}{}$\2\6 |
\&{if} ${}(\|s\E\.{'-'}){}$\1\5 |
${}\|z\MRL{{\OR}{\K}}\\{sign\_bit};{}$\2\6 |
\&{return} \|z;\6 |
\4${}\}{}$\2\par |
\fi |
|
\N{1}{41}Floating multiplication and division. |
The hardest fixed point operations were multiplication and division; |
but these two operations are the {\it easiest\/} to implement in floating point |
arithmetic, once their fixed point counterparts are available. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{fmult}\,\,${}\.{ARGS}((\&{octa},\39\&{octa})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{fmult}(\|y,\39\|z){}$\1\1\6 |
\&{octa} \|y${},{}$ \|z;\2\2\6 |
${}\{{}$\1\6 |
\&{ftype} \\{yt}${},{}$ \\{zt};\6 |
\&{int} \\{ye}${},{}$ \\{ze};\6 |
\&{char} \\{ys}${},{}$ \\{zs};\6 |
\&{octa} \|x${},{}$ \\{xf}${},{}$ \\{yf}${},{}$ \\{zf};\6 |
\&{register} \&{int} \\{xe};\6 |
\&{register} \&{char} \\{xs};\7 |
${}\\{yt}\K\\{funpack}(\|y,\39{\AND}\\{yf},\39{\AND}\\{ye},\39{\AND}\\{ys});{}$% |
\6 |
${}\\{zt}\K\\{funpack}(\|z,\39{\AND}\\{zf},\39{\AND}\\{ze},\39{\AND}\\{zs});{}$% |
\6 |
${}\\{xs}\K\\{ys}+\\{zs}-\.{'+'}{}$;\C{ will be \PB{\.{'-'}} when the result is |
negative }\6 |
\&{switch} ${}(\T{4}*\\{yt}+\\{zt}){}$\5 |
${}\{{}$\1\6 |
\hbox{\4}\X42:The usual NaN cases\X;\6 |
\4\&{case} \T{4}${}*\\{zro}+\\{zro}{}$:\5 |
\&{case} \T{4}${}*\\{zro}+\\{num}{}$:\5 |
\&{case} \T{4}${}*\\{num}+\\{zro}{}$:\5 |
${}\|x\K\\{zero\_octa}{}$;\5 |
\&{break};\6 |
\4\&{case} \T{4}${}*\\{num}+\\{inf}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{num}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{inf}{}$:\5 |
${}\|x\K\\{inf\_octa}{}$;\5 |
\&{break};\6 |
\4\&{case} \T{4}${}*\\{zro}+\\{inf}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{zro}{}$:\5 |
${}\|x\K\\{standard\_NaN};{}$\6 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{I\_BIT}{}$;\5 |
\&{break};\6 |
\4\&{case} \T{4}${}*\\{num}+\\{num}{}$:\5 |
\X43:Multiply nonzero numbers and \PB{\&{return}}\X;\6 |
\4${}\}{}$\2\6 |
\&{if} ${}(\\{xs}\E\.{'-'}){}$\1\5 |
${}\|x.\|h\MRL{{\OR}{\K}}\\{sign\_bit};{}$\2\6 |
\&{return} \|x;\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{42}\B\X42:The usual NaN cases\X${}\E{}$\6 |
\4\&{case} \T{4}${}*\\{nan}+\\{nan}{}$:\5 |
\&{if} ${}(\R(\|y.\|h\AND\T{\^80000})){}$\1\5 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{I\_BIT}{}$;\C{ \PB{\|y} is signaling }\2\6 |
\4\&{case} \T{4}${}*\\{zro}+\\{nan}{}$:\5 |
\&{case} \T{4}${}*\\{num}+\\{nan}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{nan}{}$:\6 |
\&{if} ${}(\R(\|z.\|h\AND\T{\^80000})){}$\1\5 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{I\_BIT},\39\|z.\|h\MRL{{\OR}{\K}}\T{% |
\^80000};{}$\2\6 |
\&{return} \|z;\6 |
\4\&{case} \T{4}${}*\\{nan}+\\{zro}{}$:\5 |
\&{case} \T{4}${}*\\{nan}+\\{num}{}$:\5 |
\&{case} \T{4}${}*\\{nan}+\\{inf}{}$:\6 |
\&{if} ${}(\R(\|y.\|h\AND\T{\^80000})){}$\1\5 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{I\_BIT},\39\|y.\|h\MRL{{\OR}{\K}}\T{% |
\^80000};{}$\2\6 |
\&{return} \|y;\par |
\Us41, 44, 46\ETs93.\fi |
|
\M{43}\B\X43:Multiply nonzero numbers and \PB{\&{return}}\X${}\E{}$\6 |
$\\{xe}\K\\{ye}+\\{ze}-\T{\^3fd}{}$;\C{ the raw exponent }\6 |
${}\|x\K\\{omult}(\\{yf},\39\\{shift\_left}(\\{zf},\39\T{9}));{}$\6 |
\&{if} ${}(\\{aux}.\|h\G\T{\^400000}){}$\1\5 |
${}\\{xf}\K\\{aux};{}$\2\6 |
\&{else}\1\5 |
${}\\{xf}\K\\{shift\_left}(\\{aux},\39\T{1}),\39\\{xe}\MM;{}$\2\6 |
\&{if} ${}(\|x.\|h\V\|x.\|l){}$\1\5 |
${}\\{xf}.\|l\MRL{{\OR}{\K}}\T{1}{}$;\C{ adjust the sticky bit }\2\6 |
\&{return} \\{fpack}${}(\\{xf},\39\\{xe},\39\\{xs},\39\\{cur\_round}){}$;\par |
\U41.\fi |
|
\M{44}\B\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{fdivide}\,\,${}\.{ARGS}((\&{octa},\39\&{octa})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{fdivide}(\|y,\39\|z){}$\1\1\6 |
\&{octa} \|y${},{}$ \|z;\2\2\6 |
${}\{{}$\1\6 |
\&{ftype} \\{yt}${},{}$ \\{zt};\6 |
\&{int} \\{ye}${},{}$ \\{ze};\6 |
\&{char} \\{ys}${},{}$ \\{zs};\6 |
\&{octa} \|x${},{}$ \\{xf}${},{}$ \\{yf}${},{}$ \\{zf};\6 |
\&{register} \&{int} \\{xe};\6 |
\&{register} \&{char} \\{xs};\7 |
${}\\{yt}\K\\{funpack}(\|y,\39{\AND}\\{yf},\39{\AND}\\{ye},\39{\AND}\\{ys});{}$% |
\6 |
${}\\{zt}\K\\{funpack}(\|z,\39{\AND}\\{zf},\39{\AND}\\{ze},\39{\AND}\\{zs});{}$% |
\6 |
${}\\{xs}\K\\{ys}+\\{zs}-\.{'+'}{}$;\C{ will be \PB{\.{'-'}} when the result is |
negative }\6 |
\&{switch} ${}(\T{4}*\\{yt}+\\{zt}){}$\5 |
${}\{{}$\1\6 |
\hbox{\4}\X42:The usual NaN cases\X;\6 |
\4\&{case} \T{4}${}*\\{zro}+\\{inf}{}$:\5 |
\&{case} \T{4}${}*\\{zro}+\\{num}{}$:\5 |
\&{case} \T{4}${}*\\{num}+\\{inf}{}$:\5 |
${}\|x\K\\{zero\_octa}{}$;\5 |
\&{break};\6 |
\4\&{case} \T{4}${}*\\{num}+\\{zro}{}$:\5 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{Z\_BIT};{}$\6 |
\4\&{case} \T{4}${}*\\{inf}+\\{num}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{zro}{}$:\5 |
${}\|x\K\\{inf\_octa}{}$;\5 |
\&{break};\6 |
\4\&{case} \T{4}${}*\\{zro}+\\{zro}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{inf}{}$:\5 |
${}\|x\K\\{standard\_NaN};{}$\6 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{I\_BIT}{}$;\5 |
\&{break};\6 |
\4\&{case} \T{4}${}*\\{num}+\\{num}{}$:\5 |
\X45:Divide nonzero numbers and \PB{\&{return}}\X;\6 |
\4${}\}{}$\2\6 |
\&{if} ${}(\\{xs}\E\.{'-'}){}$\1\5 |
${}\|x.\|h\MRL{{\OR}{\K}}\\{sign\_bit};{}$\2\6 |
\&{return} \|x;\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{45}\B\X45:Divide nonzero numbers and \PB{\&{return}}\X${}\E{}$\6 |
$\\{xe}\K\\{ye}-\\{ze}+\T{\^3fd}{}$;\C{ the raw exponent }\6 |
${}\\{xf}\K\\{odiv}(\\{yf},\39\\{zero\_octa},\39\\{shift\_left}(\\{zf},\39% |
\T{9}));{}$\6 |
\&{if} ${}(\\{xf}.\|h\G\T{\^800000}){}$\5 |
${}\{{}$\1\6 |
${}\\{aux}.\|l\MRL{{\OR}{\K}}\\{xf}.\|l\AND\T{1};{}$\6 |
${}\\{xf}\K\\{shift\_right}(\\{xf},\39\T{1},\39\T{1});{}$\6 |
${}\\{xe}\PP;{}$\6 |
\4${}\}{}$\2\6 |
\&{if} ${}(\\{aux}.\|h\V\\{aux}.\|l){}$\1\5 |
${}\\{xf}.\|l\MRL{{\OR}{\K}}\T{1}{}$;\C{ adjust the sticky bit }\2\6 |
\&{return} \\{fpack}${}(\\{xf},\39\\{xe},\39\\{xs},\39\\{cur\_round}){}$;\par |
\U44.\fi |
|
\N{1}{46}Floating addition and subtraction. Now for the bread-and-butter |
operation, the sum of two floating point numbers. |
It is not terribly difficult, but many cases need to be handled carefully. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{fplus}\,\,${}\.{ARGS}((\&{octa},\39\&{octa})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{fplus}(\|y,\39\|z){}$\1\1\6 |
\&{octa} \|y${},{}$ \|z;\2\2\6 |
${}\{{}$\1\6 |
\&{ftype} \\{yt}${},{}$ \\{zt};\6 |
\&{int} \\{ye}${},{}$ \\{ze};\6 |
\&{char} \\{ys}${},{}$ \\{zs};\6 |
\&{octa} \|x${},{}$ \\{xf}${},{}$ \\{yf}${},{}$ \\{zf};\6 |
\&{register} \&{int} \\{xe}${},{}$ \|d;\6 |
\&{register} \&{char} \\{xs};\7 |
${}\\{yt}\K\\{funpack}(\|y,\39{\AND}\\{yf},\39{\AND}\\{ye},\39{\AND}\\{ys});{}$% |
\6 |
${}\\{zt}\K\\{funpack}(\|z,\39{\AND}\\{zf},\39{\AND}\\{ze},\39{\AND}\\{zs});{}$% |
\6 |
\&{switch} ${}(\T{4}*\\{yt}+\\{zt}){}$\5 |
${}\{{}$\1\6 |
\hbox{\4}\X42:The usual NaN cases\X;\6 |
\4\&{case} \T{4}${}*\\{zro}+\\{num}{}$:\5 |
\&{return} \\{fpack}${}(\\{zf},\39\\{ze},\39\\{zs},\39\.{ROUND\_OFF}){}$;\5 |
\&{break};\C{ may underflow }\6 |
\4\&{case} \T{4}${}*\\{num}+\\{zro}{}$:\5 |
\&{return} \\{fpack}${}(\\{yf},\39\\{ye},\39\\{ys},\39\.{ROUND\_OFF}){}$;\5 |
\&{break};\C{ may underflow }\6 |
\4\&{case} \T{4}${}*\\{inf}+\\{inf}{}$:\5 |
\&{if} ${}(\\{ys}\I\\{zs}){}$\5 |
${}\{{}$\1\6 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{I\_BIT}{}$;\5 |
${}\|x\K\\{standard\_NaN}{}$;\5 |
${}\\{xs}\K\\{zs}{}$;\5 |
\&{break};\6 |
\4${}\}{}$\2\6 |
\4\&{case} \T{4}${}*\\{num}+\\{inf}{}$:\5 |
\&{case} \T{4}${}*\\{zro}+\\{inf}{}$:\5 |
${}\|x\K\\{inf\_octa}{}$;\5 |
${}\\{xs}\K\\{zs}{}$;\5 |
\&{break};\6 |
\4\&{case} \T{4}${}*\\{inf}+\\{num}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{zro}{}$:\5 |
${}\|x\K\\{inf\_octa}{}$;\5 |
${}\\{xs}\K\\{ys}{}$;\5 |
\&{break};\6 |
\4\&{case} \T{4}${}*\\{num}+\\{num}{}$:\5 |
\&{if} ${}(\|y.\|h\I(\|z.\|h\XOR\T{\^80000000})\V\|y.\|l\I\|z.\|l){}$\1\5 |
\X47:Add nonzero numbers and \PB{\&{return}}\X;\2\6 |
\4\&{case} \T{4}${}*\\{zro}+\\{zro}{}$:\5 |
${}\|x\K\\{zero\_octa};{}$\6 |
${}\\{xs}\K(\\{ys}\E\\{zs}\?\\{ys}:\\{cur\_round}\E\.{ROUND\_DOWN}\?\.{'-'}:% |
\.{'+'}){}$;\5 |
\&{break};\6 |
\4${}\}{}$\2\6 |
\&{if} ${}(\\{xs}\E\.{'-'}){}$\1\5 |
${}\|x.\|h\MRL{{\OR}{\K}}\\{sign\_bit};{}$\2\6 |
\&{return} \|x;\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{47}\B\X47:Add nonzero numbers and \PB{\&{return}}\X${}\E{}$\6 |
${}\{{}$\5 |
\1\&{octa} \|o${},{}$ \\{oo};\7 |
\&{if} ${}(\\{ye}<\\{ze}\V(\\{ye}\E\\{ze}\W(\\{yf}.\|h<\\{zf}.\|h\V(\\{yf}.\|h% |
\E\\{zf}.\|h\W\\{yf}.\|l<\\{zf}.\|l)))){}$\1\5 |
\X48:Exchange \PB{\|y} with \PB{\|z}\X;\2\6 |
${}\|d\K\\{ye}-\\{ze};{}$\6 |
${}\\{xs}\K\\{ys},\39\\{xe}\K\\{ye};{}$\6 |
\&{if} (\|d)\1\5 |
\X49:Adjust for difference in exponents\X;\2\6 |
\&{if} ${}(\\{ys}\E\\{zs}){}$\5 |
${}\{{}$\1\6 |
${}\\{xf}\K\\{oplus}(\\{yf},\39\\{zf});{}$\6 |
\&{if} ${}(\\{xf}.\|h\G\T{\^800000}){}$\1\5 |
${}\\{xe}\PP,\39\|d\K\\{xf}.\|l\AND\T{1},\39\\{xf}\K\\{shift\_right}(\\{xf},\39% |
\T{1},\39\T{1}),\39\\{xf}.\|l\MRL{{\OR}{\K}}\|d;{}$\2\6 |
\4${}\}{}$\5 |
\2\&{else}\5 |
${}\{{}$\1\6 |
${}\\{xf}\K\\{ominus}(\\{yf},\39\\{zf});{}$\6 |
\&{if} ${}(\\{xf}.\|h\G\T{\^800000}){}$\1\5 |
${}\\{xe}\PP,\39\|d\K\\{xf}.\|l\AND\T{1},\39\\{xf}\K\\{shift\_right}(\\{xf},\39% |
\T{1},\39\T{1}),\39\\{xf}.\|l\MRL{{\OR}{\K}}\|d;{}$\2\6 |
\&{else}\5 |
\1\&{while} ${}(\\{xf}.\|h<\T{\^400000}){}$\1\5 |
${}\\{xe}\MM,\39\\{xf}\K\\{shift\_left}(\\{xf},\39\T{1});{}$\2\2\6 |
\4${}\}{}$\2\6 |
\&{return} \\{fpack}${}(\\{xf},\39\\{xe},\39\\{xs},\39\\{cur\_round});{}$\6 |
\4${}\}{}$\2\par |
\U46.\fi |
|
\M{48}\B\X48:Exchange \PB{\|y} with \PB{\|z}\X${}\E{}$\6 |
${}\{{}$\1\6 |
${}\|o\K\\{yf},\39\\{yf}\K\\{zf},\39\\{zf}\K\|o;{}$\6 |
${}\|d\K\\{ye},\39\\{ye}\K\\{ze},\39\\{ze}\K\|d;{}$\6 |
${}\|d\K\\{ys},\39\\{ys}\K\\{zs},\39\\{zs}\K\|d;{}$\6 |
\4${}\}{}$\2\par |
\Us47\ET51.\fi |
|
\M{49}Proper rounding requires two bits to the right of the fraction delivered |
to~\PB{\\{fpack}}. The first is the true next bit of the result; |
the other is a ``sticky'' bit, which is nonzero if any further bits of the |
true result are nonzero. Sticky rounding to an integer takes |
$x$ into the number $\lfloor x/2\rfloor+\lceil x/2\rceil$. |
|
Some subtleties need to be observed here, in order to |
prevent the sticky bit from being shifted left. If we did not |
shift \PB{\\{yf}} left~1 before shifting \PB{\\{zf}} to the right, an incorrect |
answer would be obtained in certain cases---for example, if |
$\PB{\\{yf}}=2^{54}$, $\PB{\\{zf}}=2^{54}+2^{53}-1$, $d=52$. |
|
\Y\B\4\X49:Adjust for difference in exponents\X${}\E{}$\6 |
${}\{{}$\1\6 |
\&{if} ${}(\|d\Z\T{2}){}$\1\5 |
${}\\{zf}\K\\{shift\_right}(\\{zf},\39\|d,\39\T{1}){}$;\C{ exact result }\2\6 |
\&{else} \&{if} ${}(\|d>\T{53}){}$\1\5 |
${}\\{zf}.\|h\K\T{0},\39\\{zf}.\|l\K\T{1}{}$;\C{ tricky but OK }\2\6 |
\&{else}\5 |
${}\{{}$\1\6 |
\&{if} ${}(\\{ys}\I\\{zs}){}$\1\5 |
${}\|d\MM,\39\\{xe}\MM,\39\\{yf}\K\\{shift\_left}(\\{yf},\39\T{1});{}$\2\6 |
${}\|o\K\\{zf};{}$\6 |
${}\\{zf}\K\\{shift\_right}(\|o,\39\|d,\39\T{1});{}$\6 |
${}\\{oo}\K\\{shift\_left}(\\{zf},\39\|d);{}$\6 |
\&{if} ${}(\\{oo}.\|l\I\|o.\|l\V\\{oo}.\|h\I\|o.\|h){}$\1\5 |
${}\\{zf}.\|l\MRL{{\OR}{\K}}\T{1};{}$\2\6 |
\4${}\}{}$\2\6 |
\4${}\}{}$\2\par |
\U47.\fi |
|
\M{50}The comparison of floating point numbers with respect to $\epsilon$ |
shares some of the characteristics of floating point addition/subtraction. |
In some ways it is simpler, and in other ways it is more difficult; |
we might as well deal with it now. % anyways |
|
Subroutine \PB{$\\{fepscomp}(\|y,\|z,\|e,\|s)$} returns 2 if \PB{\|y}, \PB{% |
\|z}, or \PB{\|e} is a NaN |
or \PB{\|e} is negative. It returns 1 if \PB{$\|s\K\T{0}$} and $y\approx z\ |
(e)$ or if |
\PB{$\|s\I\T{0}$} and $y\sim z\ (e)$, |
as defined in Section~4.2.2 of {\sl Seminumerical Algorithms\/}; |
otherwise it returns~0. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{int} \\{fepscomp}\,\,${}\.{ARGS}((\&{octa},\39\&{octa},\39\&{octa},\39% |
\&{int})){}$;\5 |
\hbox{}\6{}\&{int} ${}\\{fepscomp}(\|y,\39\|z,\39\|e,\39\|s){}$\1\1\6 |
\&{octa} \|y${},{}$ \|z${},{}$ \|e;\C{ the operands }\6 |
\&{int} \|s;\C{ test similarity? }\2\2\6 |
${}\{{}$\1\6 |
\&{octa} \\{yf}${},{}$ \\{zf}${},{}$ \\{ef}${},{}$ \|o${},{}$ \\{oo};\6 |
\&{int} \\{ye}${},{}$ \\{ze}${},{}$ \\{ee};\6 |
\&{char} \\{ys}${},{}$ \\{zs}${},{}$ \\{es};\6 |
\&{register} \&{int} \\{yt}${},{}$ \\{zt}${},{}$ \\{et}${},{}$ \|d;\7 |
${}\\{et}\K\\{funpack}(\|e,\39{\AND}\\{ef},\39{\AND}\\{ee},\39{\AND}\\{es});{}$% |
\6 |
\&{if} ${}(\\{es}\E\.{'-'}){}$\1\5 |
\&{return} \T{2};\2\6 |
\&{switch} (\\{et})\5 |
${}\{{}$\1\6 |
\4\&{case} \\{nan}:\5 |
\&{return} \T{2};\6 |
\4\&{case} \\{inf}:\5 |
${}\\{ee}\K\T{10000};{}$\6 |
\4\&{case} \\{num}:\5 |
\&{case} \\{zro}:\5 |
\&{break};\6 |
\4${}\}{}$\2\6 |
${}\\{yt}\K\\{funpack}(\|y,\39{\AND}\\{yf},\39{\AND}\\{ye},\39{\AND}\\{ys});{}$% |
\6 |
${}\\{zt}\K\\{funpack}(\|z,\39{\AND}\\{zf},\39{\AND}\\{ze},\39{\AND}\\{zs});{}$% |
\6 |
\&{switch} ${}(\T{4}*\\{yt}+\\{zt}){}$\5 |
${}\{{}$\1\6 |
\4\&{case} \T{4}${}*\\{nan}+\\{nan}{}$:\5 |
\&{case} \T{4}${}*\\{nan}+\\{inf}{}$:\5 |
\&{case} \T{4}${}*\\{nan}+\\{num}{}$:\5 |
\&{case} \T{4}${}*\\{nan}+\\{zro}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{nan}{}$:\5 |
\&{case} \T{4}${}*\\{num}+\\{nan}{}$:\5 |
\&{case} \T{4}${}*\\{zro}+\\{nan}{}$:\5 |
\&{return} \T{2};\6 |
\4\&{case} \T{4}${}*\\{inf}+\\{inf}{}$:\5 |
\&{return} ${}(\\{ys}\E\\{zs}\V\\{ee}\G\T{1023});{}$\6 |
\4\&{case} \T{4}${}*\\{inf}+\\{num}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{zro}{}$:\5 |
\&{case} \T{4}${}*\\{num}+\\{inf}{}$:\5 |
\&{case} \T{4}${}*\\{zro}+\\{inf}{}$:\5 |
\&{return} ${}(\|s\W\\{ee}\G\T{1022});{}$\6 |
\4\&{case} \T{4}${}*\\{zro}+\\{zro}{}$:\5 |
\&{return} \T{1};\6 |
\4\&{case} \T{4}${}*\\{zro}+\\{num}{}$:\5 |
\&{case} \T{4}${}*\\{num}+\\{zro}{}$:\5 |
\&{if} ${}(\R\|s){}$\1\5 |
\&{return} \T{0};\2\6 |
\4\&{case} \T{4}${}*\\{num}+\\{num}{}$:\5 |
\&{break};\6 |
\4${}\}{}$\2\6 |
\X51:Compare two numbers with respect to epsilon and \PB{\&{return}}\X;\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{51}The relation $y\approx z\ (\epsilon)$ reduces to |
$y\sim z\ (\epsilon/2^d)$, if $d$~is the difference between the |
larger and smaller exponents of $y$ and~$z$. |
|
\Y\B\4\X51:Compare two numbers with respect to epsilon and \PB{\&{return}}\X${}% |
\E{}$\6 |
\X52:Unsubnormalize \PB{\|y} and \PB{\|z}, if they are subnormal\X;\6 |
\&{if} ${}(\\{ye}<\\{ze}\V(\\{ye}\E\\{ze}\W(\\{yf}.\|h<\\{zf}.\|h\V(\\{yf}.\|h% |
\E\\{zf}.\|h\W\\{yf}.\|l<\\{zf}.\|l)))){}$\1\5 |
\X48:Exchange \PB{\|y} with \PB{\|z}\X;\2\6 |
\&{if} ${}(\\{ze}\E\\{zero\_exponent}){}$\1\5 |
${}\\{ze}\K\\{ye};{}$\2\6 |
${}\|d\K\\{ye}-\\{ze};{}$\6 |
\&{if} ${}(\R\|s){}$\1\5 |
${}\\{ee}\MRL{-{\K}}\|d;{}$\2\6 |
\&{if} ${}(\\{ee}\G\T{1023}){}$\1\5 |
\&{return} \T{1};\C{ if $\epsilon\ge2$, $z\in N_\epsilon(y)$ }\2\6 |
\X53:Compute the difference of fraction parts, \PB{\|o}\X;\6 |
\&{if} ${}(\R\|o.\|h\W\R\|o.\|l){}$\1\5 |
\&{return} \T{1};\2\6 |
\&{if} ${}(\\{ee}<\T{968}){}$\1\5 |
\&{return} \T{0};\C{ if $y\ne z$ and $\epsilon<2^{-54}$, $y\not\sim z$ }\2\6 |
\&{if} ${}(\\{ee}\G\T{1021}){}$\1\5 |
${}\\{ef}\K\\{shift\_left}(\\{ef},\39\\{ee}-\T{1021});{}$\2\6 |
\&{else}\1\5 |
${}\\{ef}\K\\{shift\_right}(\\{ef},\39\T{1021}-\\{ee},\39\T{1});{}$\2\6 |
\&{return} \|o${}.\|h<\\{ef}.\|h\V(\|o.\|h\E\\{ef}.\|h\W\|o.\|l\Z\\{ef}.% |
\|l){}$;\par |
\U50.\fi |
|
\M{52}\B\X52:Unsubnormalize \PB{\|y} and \PB{\|z}, if they are subnormal\X${}% |
\E{}$\6 |
\&{if} ${}(\\{ye}<\T{0}\W\\{yt}\I\\{zro}){}$\1\5 |
${}\\{yf}\K\\{shift\_left}(\|y,\39\T{2}),\39\\{ye}\K\T{0};{}$\2\6 |
\&{if} ${}(\\{ze}<\T{0}\W\\{zt}\I\\{zro}){}$\1\5 |
${}\\{zf}\K\\{shift\_left}(\|z,\39\T{2}),\39\\{ze}\K\T{0}{}$;\2\par |
\U51.\fi |
|
\M{53}At this point $y\sim z$ if and only if |
$$\PB{\\{yf}}+(-1)^{[ys=zs]}\PB{\\{zf}}/2^d\le 2^{ee-1021}\PB{\\{ef}}=2^{55}% |
\epsilon.$$ |
We need to evaluate this relation without overstepping the bounds of |
our simulated 64-bit registers. |
|
When $d>2$, the difference of fraction parts might not fit exactly |
in an octabyte; |
in that case the numbers are not similar unless $\epsilon>3/8$, |
and we replace the difference by the ceiling of the |
true result. When $\epsilon<1/8$, our program essentially replaces |
$2^{55}\epsilon$ by $\lfloor2^{55}\epsilon\rfloor$. These |
truncations are not needed simultaneously. Therefore the logic |
is justified by the facts that, if $n$ is an integer, we have |
$x\le n$ if and only if $\lceil x\rceil\le n$; |
$n\le x$ if and only if $n\le\lfloor x\rfloor$. (Notice that the |
concept of ``sticky bit'' is {\it not\/} appropriate here.) |
|
\Y\B\4\X53:Compute the difference of fraction parts, \PB{\|o}\X${}\E{}$\6 |
\&{if} ${}(\|d>\T{54}){}$\1\5 |
${}\|o\K\\{zero\_octa},\39\\{oo}\K\\{zf};{}$\2\6 |
\&{else}\1\5 |
${}\|o\K\\{shift\_right}(\\{zf},\39\|d,\39\T{1}),\39\\{oo}\K\\{shift\_left}(% |
\|o,\39\|d);{}$\2\6 |
\&{if} ${}(\\{oo}.\|h\I\\{zf}.\|h\V\\{oo}.\|l\I\\{zf}.\|l){}$\5 |
${}\{{}$\C{ truncated result, hence $d>2$ }\1\6 |
\&{if} ${}(\\{ee}<\T{1020}){}$\1\5 |
\&{return} \T{0};\C{ difference is too large for similarity }\2\6 |
${}\|o\K\\{incr}(\|o,\39\\{ys}\E\\{zs}\?\T{0}:\T{1}){}$;\C{ adjust for ceiling |
}\6 |
\4${}\}{}$\2\6 |
${}\|o\K(\\{ys}\E\\{zs}\?\\{ominus}(\\{yf},\39\|o):\\{oplus}(\\{yf},\39% |
\|o)){}$;\par |
\U51.\fi |
|
\N{1}{54}Floating point output conversion. |
The \PB{\\{print\_float}} routine converts an octabyte to a floating decimal |
representation that will be input as precisely the same value. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{static} \&{void} \\{bignum\_times\_ten}\,\,${}\.{ARGS}((\\{bignum}*));{}$\6 |
\&{static} \&{void} \\{bignum\_dec}\,\,${}\.{ARGS}((\\{bignum}*,\\{bignum}*,% |
\&{tetra}));{}$\6 |
\&{static} \&{int} \\{bignum\_compare}\,\,${}\.{ARGS}((\\{bignum}*,% |
\\{bignum}*));{}$\6 |
\&{void} \\{print\_float}\,\,\.{ARGS}((\&{octa}));\5 |
\hbox{}\6{}\&{void} \\{print\_float}(\|x)\1\1\6 |
\&{octa} \|x;\2\2\6 |
${}\{{}$\1\6 |
\X56:Local variables for \PB{\\{print\_float}}\X;\6 |
\&{if} ${}(\|x.\|h\AND\\{sign\_bit}){}$\1\5 |
\\{printf}(\.{"-"});\2\6 |
\X55:Extract the exponent \PB{\|e} and determine the fraction interval $[f\dts |
g]$ or $(f\dts g)$\X;\6 |
\X63:Store $f$ and $g$ as multiprecise integers\X;\6 |
\X64:Compute the significant digits \PB{\|s} and decimal exponent \PB{\|e}\X;\6 |
\X67:Print the significant digits with proper context\X;\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{55}One way to visualize the problem being solved here is to consider |
the vastly simpler case in which there are only 2-bit exponents |
and 2-bit fractions. Then the sixteen possible 4-bit combinations |
have the following interpretations: |
$$\def\\{\;\dts\;} |
\vbox{\halign{#\qquad&$#$\hfil\cr |
0000&[0\\0.125]\cr |
0001&(0.125\\0.375)\cr |
0010&[0.375\\0.625]\cr |
0011&(0.625\\0.875)\cr |
0100&[0.875\\1.125]\cr |
0101&(1.125\\1.375)\cr |
0110&[1.375\\1.625]\cr |
0111&(1.625\\1.875)\cr |
1000&[1.875\\2.25]\cr |
1001&(2.25\\2.75)\cr |
1010&[2.75\\3.25]\cr |
1011&(3.25\\3.75)\cr |
1100&[3.75\\\infty]\cr |
1101&\rm NaN(0\\0.375)\cr |
1110&\rm NaN[0.375\\0.625]\cr |
1111&\rm NaN(0.625\\1)\cr}}$$ |
Notice that the interval is closed, $[f\dts g]$, when the fraction part |
is even; it is open, $(f\dts g)$, when the fraction part is odd. |
The printed outputs for these sixteen values, if we actually were |
dealing with such short exponents and fractions, would be |
\.{0.}, \.{.2}, \.{.5}, \.{.7}, \.{1.}, \.{1.2}, \.{1.5}, \.{1.7}, |
\.{2.}, \.{2.5}, \.{3.}, \.{3.5}, \.{Inf}, \.{NaN.2}, \.{NaN}, \.{NaN.8}, |
respectively. |
|
\Y\B\4\X55:Extract the exponent \PB{\|e} and determine the fraction interval |
$[f\dts g]$ or $(f\dts g)$\X${}\E{}$\6 |
$\|f\K\\{shift\_left}(\|x,\39\T{1});{}$\6 |
${}\|e\K\|f.\|h\GG\T{21};{}$\6 |
${}\|f.\|h\MRL{\AND{\K}}\T{\^1fffff};{}$\6 |
\&{if} ${}(\R\|f.\|h\W\R\|f.\|l){}$\1\5 |
\X57:Handle the special case when the fraction part is zero\X\2\6 |
\&{else}\5 |
${}\{{}$\1\6 |
${}\|g\K\\{incr}(\|f,\39\T{1});{}$\6 |
${}\|f\K\\{incr}(\|f,\39{-}\T{1});{}$\6 |
\&{if} ${}(\R\|e){}$\1\5 |
${}\|e\K\T{1}{}$;\C{ subnormal }\2\6 |
\&{else} \&{if} ${}(\|e\E\T{\^7ff}){}$\5 |
${}\{{}$\1\6 |
\\{printf}(\.{"NaN"});\6 |
\&{if} ${}(\|g.\|h\E\T{\^100000}\W\|g.\|l\E\T{1}){}$\1\5 |
\&{return};\C{ the ``standard'' NaN }\2\6 |
${}\|e\K\T{\^3ff}{}$;\C{ extreme NaNs come out OK even without adjusting \PB{% |
\|f} or \PB{\|g} }\6 |
\4${}\}{}$\5 |
\2\&{else}\1\5 |
${}\|f.\|h\MRL{{\OR}{\K}}\T{\^200000},\39\|g.\|h\MRL{{\OR}{\K}}\T{\^200000};{}$% |
\2\6 |
\4${}\}{}$\2\par |
\U54.\fi |
|
\M{56}\B\X56:Local variables for \PB{\\{print\_float}}\X${}\E{}$\6 |
\&{octa} \|f${},{}$ \|g;\C{ lower and upper bounds on the fraction part }\6 |
\&{register} \&{int} \|e;\C{ exponent part }\6 |
\&{register} \&{int} \|j${},{}$ \|k;\C{ all purpose indices }\par |
\A66. |
\U54.\fi |
|
\M{57}The transition points between exponents correspond to powers of~2. At |
such points the interval extends only half as far to the left of that |
power of~2 as it does to the right. For example, in the 4-bit minifloat numbers |
considered above, case 1000 corresponds to the interval $[1.875\;\dts\;2.25]$. |
|
\Y\B\4\X57:Handle the special case when the fraction part is zero\X${}\E{}$\6 |
${}\{{}$\1\6 |
\&{if} ${}(\R\|e){}$\5 |
${}\{{}$\1\6 |
\\{printf}(\.{"0."});\5 |
\&{return};\6 |
\4${}\}{}$\2\6 |
\&{if} ${}(\|e\E\T{\^7ff}){}$\5 |
${}\{{}$\1\6 |
\\{printf}(\.{"Inf"});\5 |
\&{return};\6 |
\4${}\}{}$\2\6 |
${}\|e\MM;{}$\6 |
${}\|f.\|h\K\T{\^3fffff},\39\|f.\|l\K\T{\^ffffffff};{}$\6 |
${}\|g.\|h\K\T{\^400000},\39\|g.\|l\K\T{2};{}$\6 |
\4${}\}{}$\2\par |
\U55.\fi |
|
\M{58}We want to find the ``simplest'' value in the interval corresponding |
to the given number, in the sense that it has fewest significant |
digits when expressed in decimal notation. Thus, for example, |
if the floating point number can be described by a relatively |
short string such as `\.{.1}' or `\.{37e100}', we want to discover that |
representation. |
|
The basic idea is to generate the decimal representations of the |
two endpoints of the interval, outputting the leading digits where |
both endpoints agree, then making a final decision at the first place where |
they disagree. |
|
The ``simplest'' value is not always unique. For example, in the |
case of 4-bit minifloat numbers we could represent the bit pattern 0001 as |
either \.{.2} or \.{.3}, and we could represent 1001 in five equally short |
ways: \.{2.3} or \.{2.4} or \.{2.5} or \.{2.6} or \.{2.7}. The |
algorithm below tries to choose the middle possibility in such cases. |
|
[A solution to the analogous problem for fixed-point representations, |
without the additional complication of round-to-even, was used by |
the author in the program for \TeX; see {\sl Beauty is Our Business\/} |
(Springer, 1990), 233--242.] |
|
Suppose we are given two fractions $f$ and $g$, where $0\le f<g<1$, and |
we want to compute the shortest decimal in the closed interval $[f\dts g]$. |
If $f=0$, we are done. Otherwise let $10f=d+f'$ and $10g=e+g'$, where |
$0\le f'<1$ and $0\le g'<1$. If $d<e$, we can terminate by outputting |
any of the digits $d+1$, \dots,~$e$; otherwise we output the |
common digit $d=e$, and repeat the process on the fractions $0\le f'<g'<1$. |
A similar procedure works with respect to the open interval $(f\dts g)$. |
|
\fi |
|
\M{59}The program below carries out the stated algorithm by using |
multiprecision |
arithmetic on 77-place integers with 28 bits each. This choice |
facilitates multiplication by~10, and allows us to deal with the whole range of |
floating binary numbers using fixed point arithmetic. We keep track of |
the leading and trailing digit positions so that trivial operations on |
zeros are avoided. |
|
If \PB{\|f} points to a \&{bignum}, its radix-$2^{28}$ digits are |
\PB{$\|f\MG\\{dat}[\T{0}]$} through \PB{$\|f\MG\\{dat}[\T{76}]$}, from most |
significant to least significant. |
We assume that all digit positions are zero unless they lie in the |
subarray between indices \PB{$\|f\MG\|a$} and \PB{$\|f\MG\|b$}, inclusive. |
Furthermore, both \PB{$\|f\MG\\{dat}[\|f\MG\|a]$} and \PB{$\|f\MG\\{dat}[\|f\MG% |
\|b]$} are nonzero, |
unless \PB{$\|f\MG\|a\K\|f\MG\|b\K\\{bignum\_prec}-\T{1}$}. |
|
The \&{bignum} data type can be used with any radix less than |
$2^{32}$; we will use it later with radix~$10^9$. The \PB{\\{dat}} array |
is made large enough to accommodate both applications. |
|
\Y\B\4\D$\\{bignum\_prec}$ \5 |
\T{157}\C{ would be 77 if we cared only about \PB{\\{print\_float}} }\par |
\Y\B\4\X36:Other type definitions\X${}\mathrel+\E{}$\6 |
\&{typedef} \&{struct} ${}\{{}$\1\6 |
\&{int} \|a;\C{ index of the most significant digit }\6 |
\&{int} \|b;\C{ index of the least significant digit; must be $\ge a$ }\6 |
\&{tetra} \\{dat}[\\{bignum\_prec}];\C{ the digits; undefined except between % |
\PB{\|a} and \PB{\|b} }\2\6 |
${}\}{}$ \&{bignum};\par |
\fi |
|
\M{60}Here, for example, is how we go from $f$ to $10f$, assuming that |
overflow will not occur and that the radix is $2^{28}$: |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{static} \&{void} \\{bignum\_times\_ten}(\|f)\1\1\6 |
\&{bignum} ${}{*}\|f;\2\2{}$\6 |
${}\{{}$\1\6 |
\&{register} \&{tetra} ${}{*}\|p,{}$ ${}{*}\|q;{}$\6 |
\&{register} \&{tetra} \|x${},{}$ \\{carry};\7 |
\&{for} ${}(\|p\K{\AND}\|f\MG\\{dat}[\|f\MG\|b],\39\|q\K{\AND}\|f\MG\\{dat}[\|f% |
\MG\|a],\39\\{carry}\K\T{0};{}$ ${}\|p\G\|q;{}$ ${}\|p\MM){}$\5 |
${}\{{}$\1\6 |
${}\|x\K{*}\|p*\T{10}+\\{carry};{}$\6 |
${}{*}\|p\K\|x\AND\T{\^fffffff};{}$\6 |
${}\\{carry}\K\|x\GG\T{28};{}$\6 |
\4${}\}{}$\2\6 |
${}{*}\|p\K\\{carry};{}$\6 |
\&{if} (\\{carry})\1\5 |
${}\|f\MG\|a\MM;{}$\2\6 |
\&{if} ${}(\|f\MG\\{dat}[\|f\MG\|b]\E\T{0}\W\|f\MG\|b>\|f\MG\|a){}$\1\5 |
${}\|f\MG\|b\MM;{}$\2\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{61}And here is how we test whether $f<g$, $f=g$, or $f>g$, using any |
radix whatever: |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{static} \&{int} ${}\\{bignum\_compare}(\|f,\39\|g){}$\1\1\6 |
\&{bignum} ${}{*}\|f,{}$ ${}{*}\|g;\2\2{}$\6 |
${}\{{}$\1\6 |
\&{register} \&{tetra} ${}{*}\|p,{}$ ${}{*}\\{pp},{}$ ${}{*}\|q,{}$ ${}{*}% |
\\{qq};{}$\7 |
\&{if} ${}(\|f\MG\|a\I\|g\MG\|a){}$\1\5 |
\&{return} \|f${}\MG\|a>\|g\MG\|a\?{-}\T{1}:\T{1};{}$\2\6 |
${}\\{pp}\K{\AND}\|f\MG\\{dat}[\|f\MG\|b],\39\\{qq}\K{\AND}\|g\MG\\{dat}[\|g\MG% |
\|b];{}$\6 |
\&{for} ${}(\|p\K{\AND}\|f\MG\\{dat}[\|f\MG\|a],\39\|q\K{\AND}\|g\MG\\{dat}[\|g% |
\MG\|a];{}$ ${}\|p\Z\\{pp};{}$ ${}\|p\PP,\39\|q\PP){}$\5 |
${}\{{}$\1\6 |
\&{if} ${}({*}\|p\I{*}\|q){}$\1\5 |
\&{return} ${}{*}\|p<{*}\|q\?{-}\T{1}:\T{1};{}$\2\6 |
\&{if} ${}(\|q\E\\{qq}){}$\1\5 |
\&{return} \|p${}<\\{pp};{}$\2\6 |
\4${}\}{}$\2\6 |
\&{return} ${}{-}\T{1};{}$\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{62}The following subroutine subtracts $g$ from~$f$, assuming that |
$f\ge g>0$ and using a given radix. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{static} \&{void} ${}\\{bignum\_dec}(\|f,\39\|g,\39\|r){}$\1\1\6 |
\&{bignum} ${}{*}\|f,{}$ ${}{*}\|g;{}$\6 |
\&{tetra} \|r;\C{ the radix }\2\2\6 |
${}\{{}$\1\6 |
\&{register} \&{tetra} ${}{*}\|p,{}$ ${}{*}\|q,{}$ ${}{*}\\{qq};{}$\6 |
\&{register} \&{int} \|x${},{}$ \\{borrow};\7 |
\&{while} ${}(\|g\MG\|b>\|f\MG\|b){}$\1\5 |
${}\|f\MG\\{dat}[\PP\|f\MG\|b]\K\T{0};{}$\2\6 |
${}\\{qq}\K{\AND}\|g\MG\\{dat}[\|g\MG\|a];{}$\6 |
\&{for} ${}(\|p\K{\AND}\|f\MG\\{dat}[\|g\MG\|b],\39\|q\K{\AND}\|g\MG\\{dat}[\|g% |
\MG\|b],\39\\{borrow}\K\T{0};{}$ ${}\|q\G\\{qq};{}$ ${}\|p\MM,\39\|q\MM){}$\5 |
${}\{{}$\1\6 |
${}\|x\K{*}\|p-{*}\|q-\\{borrow};{}$\6 |
\&{if} ${}(\|x\G\T{0}){}$\1\5 |
${}\\{borrow}\K\T{0},\39{*}\|p\K\|x;{}$\2\6 |
\&{else}\1\5 |
${}\\{borrow}\K\T{1},\39{*}\|p\K\|x+\|r;{}$\2\6 |
\4${}\}{}$\2\6 |
\&{for} ( ; \\{borrow}; ${}\|p\MM){}$\1\6 |
\&{if} ${}({*}\|p){}$\1\5 |
${}\\{borrow}\K\T{0},\39{*}\|p\K{*}\|p-\T{1};{}$\2\6 |
\&{else}\1\5 |
${}{*}\|p\K\|r-\T{1};{}$\2\2\6 |
\&{while} ${}(\|f\MG\\{dat}[\|f\MG\|a]\E\T{0}){}$\5 |
${}\{{}$\1\6 |
\&{if} ${}(\|f\MG\|a\E\|f\MG\|b){}$\5 |
${}\{{}$\C{ the result is zero }\1\6 |
${}\|f\MG\|a\K\|f\MG\|b\K\\{bignum\_prec}-\T{1},\39\|f\MG\\{dat}[\\{bignum% |
\_prec}-\T{1}]\K\T{0};{}$\6 |
\&{return};\6 |
\4${}\}{}$\2\6 |
${}\|f\MG\|a\PP;{}$\6 |
\4${}\}{}$\2\6 |
\&{while} ${}(\|f\MG\\{dat}[\|f\MG\|b]\E\T{0}){}$\1\5 |
${}\|f\MG\|b\MM;{}$\2\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{63}Armed with these subroutines, we are ready to solve the problem. |
The first task is to put the numbers into \&{bignum} form. |
If the exponent is \PB{\|e}, the number destined for digit \PB{\\{dat}[\|k]} |
will |
consist of the rightmost 28 bits of the given fraction after it has |
been shifted right $c-e-28k$ bits, for some constant~$c$. |
We choose $c$ so that, |
when $e$ has its maximum value \Hex{7ff}, the leading digit will |
go into position \PB{\\{dat}[\T{1}]}, and so that when the number to be printed |
is exactly~1 the integer part of~$g$ will also be exactly~1. |
|
\Y\B\4\D$\\{magic\_offset}$ \5 |
\T{2112}\C{ the constant $c$ that makes it work }\par |
\B\4\D$\\{origin}$ \5 |
\T{37}\C{ the radix point follows \PB{\\{dat}[\T{37}]} }\par |
\Y\B\4\X63:Store $f$ and $g$ as multiprecise integers\X${}\E{}$\6 |
$\|k\K(\\{magic\_offset}-\|e)/\T{28};{}$\6 |
${}\ff.\\{dat}[\|k-\T{1}]\K\\{shift\_right}(\|f,\39\\{magic\_offset}+\T{28}-% |
\|e-\T{28}*\|k,\39\T{1}).\|l\AND\T{\^fffffff};{}$\6 |
${}\\{gg}.\\{dat}[\|k-\T{1}]\K\\{shift\_right}(\|g,\39\\{magic\_offset}+\T{28}-% |
\|e-\T{28}*\|k,\39\T{1}).\|l\AND\T{\^fffffff};{}$\6 |
${}\ff.\\{dat}[\|k]\K\\{shift\_right}(\|f,\39\\{magic\_offset}-\|e-\T{28}*\|k,% |
\39\T{1}).\|l\AND\T{\^fffffff};{}$\6 |
${}\\{gg}.\\{dat}[\|k]\K\\{shift\_right}(\|g,\39\\{magic\_offset}-\|e-\T{28}*% |
\|k,\39\T{1}).\|l\AND\T{\^fffffff};{}$\6 |
${}\ff.\\{dat}[\|k+\T{1}]\K\\{shift\_left}(\|f,\39\|e+\T{28}*\|k-(\\{magic% |
\_offset}-\T{28})).\|l\AND\T{\^fffffff};{}$\6 |
${}\\{gg}.\\{dat}[\|k+\T{1}]\K\\{shift\_left}(\|g,\39\|e+\T{28}*\|k-(\\{magic% |
\_offset}-\T{28})).\|l\AND\T{\^fffffff};{}$\6 |
${}\ff.\|a\K(\ff.\\{dat}[\|k-\T{1}]\?\|k-\T{1}:\|k);{}$\6 |
${}\ff.\|b\K(\ff.\\{dat}[\|k+\T{1}]\?\|k+\T{1}:\|k);{}$\6 |
${}\\{gg}.\|a\K(\\{gg}.\\{dat}[\|k-\T{1}]\?\|k-\T{1}:\|k);{}$\6 |
${}\\{gg}.\|b\K(\\{gg}.\\{dat}[\|k+\T{1}]\?\|k+\T{1}:\|k){}$;\par |
\U54.\fi |
|
\M{64}If $e$ is sufficiently small, the fractions $f$ and $g$ will be less |
than~1, |
and we can use the stated algorithm directly. Of course, if $e$ is |
extremely small, a lot of leading zeros need to be lopped off; in the |
worst case, we may have to multiply $f$ and~$g$ by~10 more than 300 times. |
But hey, we don't need to do that extremely often, and computers are |
pretty fast nowadays. |
|
In the small-exponent case, the computation always terminates before |
$f$ becomes zero, because the interval endpoints are fractions with |
denominator $2^t$ for some $t>50$. |
|
The invariant relations \PB{$\ff.\\{dat}[\ff.\|a]\I\T{0}$} and \PB{$\\{gg}.% |
\\{dat}[\\{gg}.\|a]\I\T{0}$} are |
not maintained by the computation here, when \PB{$\ff.\|a\K\\{origin}$} or % |
\PB{$\\{gg}.\|a\K\\{origin}$}. |
But no harm is done, because \PB{\\{bignum\_compare}} is not used. |
|
\Y\B\4\X64:Compute the significant digits \PB{\|s} and decimal exponent \PB{% |
\|e}\X${}\E{}$\6 |
\&{if} ${}(\|e>\T{\^401}){}$\1\5 |
\X65:Compute the significant digits in the large-exponent case\X\2\6 |
\&{else}\5 |
${}\{{}$\C{ if \PB{$\|e\Z\T{\^401}$} we have \PB{$\\{gg}.\|a\G\\{origin}$} and % |
\PB{$\\{gg}.\\{dat}[\\{origin}]\Z\T{8}$} }\1\6 |
\&{if} ${}(\ff.\|a>\\{origin}){}$\1\5 |
${}\ff.\\{dat}[\\{origin}]\K\T{0};{}$\2\6 |
\&{for} ${}(\|e\K\T{1},\39\|p\K\|s;{}$ ${}\\{gg}.\|a>\\{origin}\V\ff.\\{dat}[% |
\\{origin}]\E\\{gg}.\\{dat}[\\{origin}];{}$ \,)\5 |
${}\{{}$\1\6 |
\&{if} ${}(\\{gg}.\|a>\\{origin}){}$\1\5 |
${}\|e\MM;{}$\2\6 |
\&{else}\1\5 |
${}{*}\|p\PP\K\ff.\\{dat}[\\{origin}]+\.{'0'},\39\ff.\\{dat}[\\{origin}]\K% |
\T{0},\39\\{gg}.\\{dat}[\\{origin}]\K\T{0};{}$\2\6 |
${}\\{bignum\_times\_ten}({\AND}\ff);{}$\6 |
${}\\{bignum\_times\_ten}({\AND}\\{gg});{}$\6 |
\4${}\}{}$\2\6 |
${}{*}\|p\PP\K((\ff.\\{dat}[\\{origin}]+\T{1}+\\{gg}.\\{dat}[\\{origin}])\GG% |
\T{1})+\.{'0'}{}$;\C{ the middle digit }\6 |
\4${}\}{}$\2\6 |
${}{*}\|p\K\.{'\\0'}{}$;\C{ terminate the string \PB{\|s} }\par |
\U54.\fi |
|
\M{65}When \PB{\|e} is large, we use the stated algorithm by considering $f$ |
and |
$g$ to be fractions whose denominator is a power of~10. |
|
An interesting case arises when the number to be converted is |
\Hex{44ada56a4b0835bf}, since the interval turns out to be |
$$ (69999999999999991611392\ \ \dts\ \ 70000000000000000000000).$$ |
If this were a closed interval, we could simply give the answer |
\.{7e22}; but the number \.{7e22} actually corresponds to |
\Hex{44ada56a4b0835c0} |
because of the round-to-even rule. Therefore the correct answer is, say, |
\.{6.9999999999999995e22}. This example shows that we need a slightly |
different strategy in the case of open intervals; we cannot simply |
look at the first position in which the endpoints have different |
decimal digits. Therefore we change the invariant relation to $0\le f<g\le 1$, |
when open intervals are involved, |
and we do not terminate the process when $f=0$ or $g=1$. |
|
\Y\B\4\X65:Compute the significant digits in the large-exponent case\X${}\E{}$\6 |
${}\{{}$\5 |
\1\&{register} \&{int} \\{open}${}\K\|x.\|l\AND\T{1};{}$\7 |
${}\\{tt}.\\{dat}[\\{origin}]\K\T{10};{}$\6 |
${}\\{tt}.\|a\K\\{tt}.\|b\K\\{origin};{}$\6 |
\&{for} ${}(\|e\K\T{1};{}$ ${}\\{bignum\_compare}({\AND}\\{gg},\39{\AND}\\{tt})% |
\G\\{open};{}$ ${}\|e\PP){}$\1\5 |
${}\\{bignum\_times\_ten}({\AND}\\{tt});{}$\2\6 |
${}\|p\K\|s;{}$\6 |
\&{while} (\T{1})\5 |
${}\{{}$\1\6 |
${}\\{bignum\_times\_ten}({\AND}\ff);{}$\6 |
${}\\{bignum\_times\_ten}({\AND}\\{gg});{}$\6 |
\&{for} ${}(\|j\K\.{'0'};{}$ ${}\\{bignum\_compare}({\AND}\ff,\39{\AND}\\{tt})% |
\G\T{0};{}$ ${}\|j\PP){}$\1\5 |
${}\\{bignum\_dec}({\AND}\ff,\39{\AND}\\{tt},\39\T{\^10000000}),\39\\{bignum% |
\_dec}({\AND}\\{gg},\39{\AND}\\{tt},\39\T{\^10000000});{}$\2\6 |
\&{if} ${}(\\{bignum\_compare}({\AND}\\{gg},\39{\AND}\\{tt})\G\\{open}){}$\1\5 |
\&{break};\2\6 |
${}{*}\|p\PP\K\|j;{}$\6 |
\&{if} ${}(\ff.\|a\E\\{bignum\_prec}-\T{1}\W\R\\{open}){}$\1\5 |
\&{goto} \\{done};\C{ $f=0$ in a closed interval }\2\6 |
\4${}\}{}$\2\6 |
\&{for} ${}(\|k\K\|j;{}$ ${}\\{bignum\_compare}({\AND}\\{gg},\39{\AND}\\{tt})\G% |
\\{open};{}$ ${}\|k\PP){}$\1\5 |
${}\\{bignum\_dec}({\AND}\\{gg},\39{\AND}\\{tt},\39\T{\^10000000});{}$\2\6 |
${}{*}\|p\PP\K(\|j+\T{1}+\|k)\GG\T{1}{}$;\C{ the middle digit }\6 |
\4\\{done}:\5 |
;\6 |
\4${}\}{}$\2\par |
\U64.\fi |
|
\M{66}The length of string~\PB{\|s} will be at most 17. For if $f$ and $g$ |
agree to 17 places, we have $g/f<1+10^{-16}$; but the |
ratio $g/f$ is always $\ge(1+2^{-52}+2^{-53})/(1+2^{-52}-2^{-53}) |
>1+2\times10^{-16}$. |
|
\Y\B\4\X56:Local variables for \PB{\\{print\_float}}\X${}\mathrel+\E{}$\6 |
\&{bignum} ${}\ff,{}$ \\{gg};\C{ fractions or numerators of fractions }\6 |
\&{bignum} \\{tt};\C{ power of ten (used as the denominator) }\6 |
\&{char} \|s[\T{18}];\6 |
\&{register} \&{char} ${}{*}\|p{}$;\par |
\fi |
|
\M{67}At this point the significant digits are in string \PB{\|s}, and \PB{$% |
\|s[\T{0}]\I\.{'0'}$}. |
If we put a decimal point at the left of~\PB{\|s}, the result should |
be multiplied by $10^e$. |
|
We prefer the output `\.{300.}' to the form `\.{3e2}', and we prefer |
`\.{.03}' to `\.{3e-2}'. In general, the output will use an |
explicit exponent only if the alternative would take more than |
18~characters. |
|
\Y\B\4\X67:Print the significant digits with proper context\X${}\E{}$\6 |
\&{if} ${}(\|e>\T{17}\V\|e<{}$(\&{int}) \\{strlen}(\|s)${}-\T{17}){}$\1\5 |
${}\\{printf}(\.{"\%c\%s\%se\%d"},\39\|s[\T{0}],\39(\|s[\T{1}]\?\.{"."}:% |
\.{""}),\39\|s+\T{1},\39\|e-\T{1});{}$\2\6 |
\&{else} \&{if} ${}(\|e<\T{0}){}$\1\5 |
${}\\{printf}(\.{".\%0*d\%s"},\39{-}\|e,\39\T{0},\39\|s);{}$\2\6 |
\&{else} \&{if} ${}(\\{strlen}(\|s)\G\|e){}$\1\5 |
${}\\{printf}(\.{"\%.*s.\%s"},\39\|e,\39\|s,\39\|s+\|e);{}$\2\6 |
\&{else}\1\5 |
${}\\{printf}(\.{"\%s\%0*d."},\39\|s,\39\|e-{}$(\&{int}) \\{strlen}(\|s)${},\39% |
\T{0}){}$;\2\par |
\U54.\fi |
|
\N{1}{68}Floating point input conversion. Going the other way, we want to |
be able to convert a given decimal number into its floating binary |
equivalent. The following syntax is supported: |
$$\vbox{\halign{$#$\hfil\cr |
\<digit>\is\.0\mid\.1\mid\.2\mid\.3\mid\.4\mid |
\.5\mid\.6\mid\.7\mid\.8\mid\.9\cr |
\<digit string>\is\<digit>\mid\<digit string>\<digit>\cr |
\<decimal string>\is\<digit string>\..\mid\..\<digit string>\mid |
\<digit string>\..\<digit string>\cr |
\<optional sign>\is\<empty>\mid\.+\mid\.-\cr |
\<exponent>\is\.e\<optional sign>\<digit string>\cr |
\<optional exponent>\is\<empty>\mid\<exponent>\cr |
\<floating magnitude>\is\<digit string>\<exponent>\mid |
\<decimal string>\<optional exponent>\mid\cr |
\hskip12em \.{Inf}\mid\.{NaN}\mid\.{NaN.}\<digit string>\cr |
\<floating constant>\is\<optional sign>\<floating magnitude>\cr |
\<decimal constant>\is\<optional sign>\<digit string>\cr |
}}$$ |
For example, `\.{-3.}' is the floating constant \Hex{c008000000000000}% |
\thinspace; |
`\.{1e3}' and `\.{1000}' are both equivalent to \Hex{408f400000000000}% |
\thinspace; |
`\.{NaN}' and `\.{+NaN.5}' are both equivalent to \Hex{7ff8000000000000}. |
|
The \PB{\\{scan\_const}} routine looks at a given string and finds the |
longest initial substring that matches the syntax of either \<decimal |
constant> or \<floating constant>. It puts the corresponding value |
into the global octabyte variable~\PB{\\{val}}; it also puts the position of |
the first |
unscanned character in the global pointer variable \PB{\\{next\_char}}. |
It returns 1 if a floating constant was found, 0~if a decimal constant |
was found, $-1$ if nothing was found. A decimal constant that doesn't |
fit in an octabyte is computed modulo~$2^{64}$. |
|
The value of \PB{\\{exceptions}} set by \PB{\\{scan\_const}} is not necessarily |
correct. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{static} \&{void} \\{bignum\_double}\,\,\.{ARGS}((\&{bignum} ${}{*}));{}$\6 |
\&{int} \\{scan\_const}\,\,\.{ARGS}((\&{char} ${}{*})){}$;\5 |
\hbox{}\6{}\&{int} \\{scan\_const}(\|s)\1\1\6 |
\&{char} ${}{*}\|s;\2\2{}$\6 |
${}\{{}$\1\6 |
\X70:Local variables for \PB{\\{scan\_const}}\X;\6 |
${}\\{val}.\|h\K\\{val}.\|l\K\T{0};{}$\6 |
${}\|p\K\|s;{}$\6 |
\&{if} ${}({*}\|p\E\.{'+'}\V{*}\|p\E\.{'-'}){}$\1\5 |
${}\\{sign}\K{*}\|p\PP{}$;\5 |
\2\&{else}\1\5 |
${}\\{sign}\K\.{'+'};{}$\2\6 |
\&{if} ${}(\\{strncmp}(\|p,\39\.{"NaN"},\39\T{3})\E\T{0}){}$\1\5 |
${}\\{NaN}\K\\{true},\39\|p\MRL{+{\K}}\T{3};{}$\2\6 |
\&{else}\1\5 |
${}\\{NaN}\K\\{false};{}$\2\6 |
\&{if} ${}((\\{isdigit}({*}\|p)\W\R\\{NaN})\V({*}\|p\E\.{'.'}\W\\{isdigit}({*}(% |
\|p+\T{1})))){}$\1\5 |
\X73:Scan a number and \PB{\&{return}}\X;\2\6 |
\&{if} (\\{NaN})\1\5 |
\X71:Return the standard NaN\X;\2\6 |
\&{if} ${}(\\{strncmp}(\|p,\39\.{"Inf"},\39\T{3})\E\T{0}){}$\1\5 |
\X72:Return infinity\X;\2\6 |
\4\\{no\_const\_found}:\5 |
${}\\{next\_char}\K\|s{}$;\5 |
\&{return} ${}{-}\T{1};{}$\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{69}\B\X4:Global variables\X${}\mathrel+\E{}$\6 |
\&{octa} \\{val};\C{ value returned by \PB{\\{scan\_const}} }\6 |
\&{char} ${}{*}\\{next\_char}{}$;\C{ pointer returned by \PB{\\{scan\_const}} }% |
\par |
\fi |
|
\M{70}\B\X70:Local variables for \PB{\\{scan\_const}}\X${}\E{}$\6 |
\&{register} \&{char} ${}{*}\|p,{}$ ${}{*}\|q{}$;\C{ for string manipulations }% |
\6 |
\&{register} \&{bool} \\{NaN};\C{ are we processing a NaN? }\6 |
\&{int} \\{sign};\C{ \PB{\.{'+'}} or \PB{\.{'-'}} }\par |
\As76\ET81. |
\U68.\fi |
|
\M{71}\B\X71:Return the standard NaN\X${}\E{}$\6 |
${}\{{}$\1\6 |
${}\\{next\_char}\K\|p;{}$\6 |
${}\\{val}.\|h\K\T{\^600000},\39\\{exp}\K\T{\^3fe};{}$\6 |
\&{goto} \\{packit};\6 |
\4${}\}{}$\2\par |
\U68.\fi |
|
\M{72}\B\X72:Return infinity\X${}\E{}$\6 |
${}\{{}$\1\6 |
${}\\{next\_char}\K\|p+\T{3};{}$\6 |
\&{goto} \\{make\_it\_infinite};\6 |
\4${}\}{}$\2\par |
\U68.\fi |
|
\M{73}We saw above that a string of at most 17 digits is enough to characterize |
a floating point number, for purposes of output. But a much longer buffer |
for digits is needed when we're doing input. For example, consider the |
borderline quantity $(1+2^{-53})/2^{1022}$; its decimal expansion, when |
written out exactly, is a number with more than 750 significant digits: |
\.{2.2250738585...8125e-308}. |
If {\it any one\/} of those digits is increased, or if |
additional nonzero digits are added as in |
\.{2.2250738585...81250000001e-308}, |
the rounded value is supposed to change from \Hex{0010000000000000} |
to \Hex{0010000000000001}. |
|
We assume here that the user prefers a perfectly correct answer to |
a speedy almost-correct one, so we implement the most general case. |
|
\Y\B\4\X73:Scan a number and \PB{\&{return}}\X${}\E{}$\6 |
${}\{{}$\1\6 |
\&{for} ${}(\|q\K\\{buf0},\39\\{dec\_pt}\K{}$(\&{char} ${}{*}){}$ \T{0}; ${}% |
\\{isdigit}({*}\|p);{}$ ${}\|p\PP){}$\5 |
${}\{{}$\1\6 |
${}\\{val}\K\\{oplus}(\\{val},\39\\{shift\_left}(\\{val},\39\T{2})){}$;\C{ |
multiply by 5 }\6 |
${}\\{val}\K\\{incr}(\\{shift\_left}(\\{val},\39\T{1}),\39{*}\|p-\.{'0'});{}$\6 |
\&{if} ${}(\|q>\\{buf0}\V{*}\|p\I\.{'0'}){}$\1\6 |
\&{if} ${}(\|q<\\{buf\_max}){}$\1\5 |
${}{*}\|q\PP\K{*}\|p;{}$\2\6 |
\&{else} \&{if} ${}({*}(\|q-\T{1})\E\.{'0'}){}$\1\5 |
${}{*}(\|q-\T{1})\K{*}\|p;{}$\2\2\6 |
\4${}\}{}$\2\6 |
\&{if} (\\{NaN})\1\5 |
${}{*}\|q\PP\K\.{'1'};{}$\2\6 |
\&{if} ${}({*}\|p\E\.{'.'}){}$\1\5 |
\X74:Scan a fraction part\X;\2\6 |
${}\\{next\_char}\K\|p;{}$\6 |
\&{if} ${}({*}\|p\E\.{'e'}\W\R\\{NaN}){}$\1\5 |
\X77:Scan an exponent\X\2\6 |
\&{else}\1\5 |
${}\\{exp}\K\T{0};{}$\2\6 |
\&{if} (\\{dec\_pt})\1\5 |
\X78:Return a floating point constant\X;\2\6 |
\&{if} ${}(\\{sign}\E\.{'-'}){}$\1\5 |
${}\\{val}\K\\{ominus}(\\{zero\_octa},\39\\{val});{}$\2\6 |
\&{return} \T{0};\6 |
\4${}\}{}$\2\par |
\U68.\fi |
|
\M{74}\B\X74:Scan a fraction part\X${}\E{}$\6 |
${}\{{}$\1\6 |
${}\\{dec\_pt}\K\|q;{}$\6 |
${}\|p\PP;{}$\6 |
\&{for} ${}(\\{zeros}\K\T{0};{}$ ${}\\{isdigit}({*}\|p);{}$ ${}\|p\PP){}$\1\6 |
\&{if} ${}({*}\|p\E\.{'0'}\W\|q\E\\{buf0}){}$\1\5 |
${}\\{zeros}\PP;{}$\2\6 |
\&{else} \&{if} ${}(\|q<\\{buf\_max}){}$\1\5 |
${}{*}\|q\PP\K{*}\|p;{}$\2\6 |
\&{else} \&{if} ${}({*}(\|q-\T{1})\E\.{'0'}){}$\1\5 |
${}{*}(\|q-\T{1})\K{*}\|p;{}$\2\2\6 |
\4${}\}{}$\2\par |
\U73.\fi |
|
\M{75}The buffer needs room for eight digits of padding at the left, followed |
by up to $1022+53-307$ significant digits, followed by a ``sticky'' digit |
at position \PB{$\\{buf\_max}-\T{1}$}, and eight more digits of padding. |
|
\Y\B\4\D$\\{buf0}$ \5 |
$(\\{buf}+\T{8}{}$)\par |
\B\4\D$\\{buf\_max}$ \5 |
$(\\{buf}+\T{777}{}$)\par |
\Y\B\4\X4:Global variables\X${}\mathrel+\E{}$\6 |
\&{static} \&{char} \\{buf}[\T{785}]${}\K\.{"00000000"}{}$;\C{ where we put |
significant input digits }\par |
\fi |
|
\M{76}\B\X70:Local variables for \PB{\\{scan\_const}}\X${}\mathrel+\E{}$\6 |
\&{register} \&{char} ${}{*}\\{dec\_pt}{}$;\C{ position of decimal point in % |
\PB{\\{buf}} }\6 |
\&{register} \&{int} \\{exp};\C{ scanned exponent; later used for raw binary |
exponent }\6 |
\&{register} \&{int} \\{zeros};\C{ leading zeros removed after decimal point }% |
\par |
\fi |
|
\M{77}Here we don't advance \PB{\\{next\_char}} and force a decimal point until |
we |
know that a syntactically correct exponent exists. |
|
The code here will convert extra-large inputs like |
`\.{9e+9999999999999999}' into $\infty$ and extra-small inputs into zero. |
Strange inputs like `\.{-00.0e9999999}' must also be accommodated. |
|
\Y\B\4\X77:Scan an exponent\X${}\E{}$\6 |
${}\{{}$\5 |
\1\&{register} \&{char} \\{exp\_sign};\7 |
${}\|p\PP;{}$\6 |
\&{if} ${}({*}\|p\E\.{'+'}\V{*}\|p\E\.{'-'}){}$\1\5 |
${}\\{exp\_sign}\K{*}\|p\PP{}$;\5 |
\2\&{else}\1\5 |
${}\\{exp\_sign}\K\.{'+'};{}$\2\6 |
\&{if} ${}(\\{isdigit}({*}\|p)){}$\5 |
${}\{{}$\1\6 |
\&{for} ${}(\\{exp}\K{*}\|p\PP-\.{'0'};{}$ ${}\\{isdigit}({*}\|p);{}$ ${}\|p% |
\PP){}$\1\6 |
\&{if} ${}(\\{exp}<\T{1000}){}$\1\5 |
${}\\{exp}\K\T{10}*\\{exp}+{*}\|p-\.{'0'};{}$\2\2\6 |
\&{if} ${}(\R\\{dec\_pt}){}$\1\5 |
${}\\{dec\_pt}\K\|q,\39\\{zeros}\K\T{0};{}$\2\6 |
\&{if} ${}(\\{exp\_sign}\E\.{'-'}){}$\1\5 |
${}\\{exp}\K{-}\\{exp};{}$\2\6 |
${}\\{next\_char}\K\|p;{}$\6 |
\4${}\}{}$\2\6 |
\4${}\}{}$\2\par |
\U73.\fi |
|
\M{78}\B\X78:Return a floating point constant\X${}\E{}$\6 |
${}\{{}$\1\6 |
\X79:Move the digits from \PB{\\{buf}} to \PB{$\ff$}\X;\6 |
\X83:Determine the binary fraction and binary exponent\X;\6 |
\4\\{packit}:\5 |
\X84:Pack and round the answer\X;\6 |
\&{return} \T{1};\6 |
\4${}\}{}$\2\par |
\U73.\fi |
|
\M{79}Now we get ready to compute the binary fraction bits, by putting the |
scanned input digits into a multiprecision fixed-point |
accumulator \PB{$\ff$} that spans the full necessary range. |
After this step, the number that we want to convert to floating binary |
will appear in \PB{$\ff.\\{dat}[\ff.\|a]$}, \PB{$\ff.\\{dat}[\ff.\|a+\T{1}]$}, % |
\dots, |
\PB{$\ff.\\{dat}[\ff.\|b]$}. |
The radix-$10^9$ digit in ${\it ff}[36-k]$ is understood to be multiplied |
by $10^{9k}$, for $36\ge k\ge-120$. |
|
\Y\B\4\X79:Move the digits from \PB{\\{buf}} to \PB{$\ff$}\X${}\E{}$\6 |
$\|x\K\\{buf}+\T{341}+\\{zeros}-\\{dec\_pt}-\\{exp};{}$\6 |
\&{if} ${}(\|q\E\\{buf0}\V\|x\G\T{1413}){}$\5 |
${}\{{}$\1\6 |
\4\\{make\_it\_zero}:\5 |
${}\\{exp}\K{-}\T{99999}{}$;\5 |
\&{goto} \\{packit};\6 |
\4${}\}{}$\2\6 |
\&{if} ${}(\|x<\T{0}){}$\5 |
${}\{{}$\1\6 |
\4\\{make\_it\_infinite}:\5 |
${}\\{exp}\K\T{99999}{}$;\5 |
\&{goto} \\{packit};\6 |
\4${}\}{}$\2\6 |
${}\ff.\|a\K\|x/\T{9};{}$\6 |
\&{for} ${}(\|p\K\|q;{}$ ${}\|p<\|q+\T{8};{}$ ${}\|p\PP){}$\1\5 |
${}{*}\|p\K\.{'0'}{}$;\C{ pad with trailing zeros }\2\6 |
${}\|q\K\|q-\T{1}-(\|q+\T{341}+\\{zeros}-\\{dec\_pt}-\\{exp})\MOD\T{9}{}$;\C{ |
compute stopping place in \PB{\\{buf}} }\6 |
\&{for} ${}(\|p\K\\{buf0}-\|x\MOD\T{9},\39\|k\K\ff.\|a;{}$ ${}\|p\Z\|q\W\|k\Z% |
\T{156};{}$ ${}\|p\MRL{+{\K}}\T{9},\39\|k\PP){}$\1\5 |
\X80:Put the 9-digit number \PB{${*}\|p$}\thinspace\dots\thinspace\PB{${*}(\|p+% |
\T{8})$} into \PB{$\ff.\\{dat}[\|k]$}\X;\2\6 |
${}\ff.\|b\K\|k-\T{1};{}$\6 |
\&{for} ${}(\|x\K\T{0};{}$ ${}\|p\Z\|q;{}$ ${}\|p\MRL{+{\K}}\T{9}){}$\1\6 |
\&{if} ${}(\\{strncmp}(\|p,\39\.{"000000000"},\39\T{9})\I\T{0}){}$\1\5 |
${}\|x\K\T{1};{}$\2\2\6 |
${}\ff.\\{dat}[\T{156}]\MRL{+{\K}}\|x{}$;\C{ nonzero digits that fall off the |
right are sticky }\6 |
\&{while} ${}(\ff.\\{dat}[\ff.\|b]\E\T{0}){}$\1\5 |
${}\ff.\|b\MM{}$;\2\par |
\U78.\fi |
|
\M{80}\B\X80:Put the 9-digit number \PB{${*}\|p$}\thinspace\dots\thinspace% |
\PB{${*}(\|p+\T{8})$} into \PB{$\ff.\\{dat}[\|k]$}\X${}\E{}$\6 |
${}\{{}$\1\6 |
\&{for} ${}(\|x\K{*}\|p-\.{'0'},\39\\{pp}\K\|p+\T{1};{}$ ${}\\{pp}<\|p+% |
\T{9};{}$ ${}\\{pp}\PP){}$\1\5 |
${}\|x\K\T{10}*\|x+{*}\\{pp}-\.{'0'};{}$\2\6 |
${}\ff.\\{dat}[\|k]\K\|x;{}$\6 |
\4${}\}{}$\2\par |
\U79.\fi |
|
\M{81}\B\X70:Local variables for \PB{\\{scan\_const}}\X${}\mathrel+\E{}$\6 |
\&{register} \&{int} \|k${},{}$ \|x;\6 |
\&{register} \&{char} ${}{*}\\{pp};{}$\6 |
\&{bignum} ${}\ff,{}$ \\{tt};\par |
\fi |
|
\M{82}Here's a subroutine that is dual to \PB{\\{bignum\_times\_ten}}. It |
changes $f$ |
to~$2f$, assuming that overflow will not occur and that the radix is $10^9$. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{static} \&{void} \\{bignum\_double}(\|f)\1\1\6 |
\&{bignum} ${}{*}\|f;\2\2{}$\6 |
${}\{{}$\1\6 |
\&{register} \&{tetra} ${}{*}\|p,{}$ ${}{*}\|q;{}$\6 |
\&{register} \&{int} \|x${},{}$ \\{carry};\7 |
\&{for} ${}(\|p\K{\AND}\|f\MG\\{dat}[\|f\MG\|b],\39\|q\K{\AND}\|f\MG\\{dat}[\|f% |
\MG\|a],\39\\{carry}\K\T{0};{}$ ${}\|p\G\|q;{}$ ${}\|p\MM){}$\5 |
${}\{{}$\1\6 |
${}\|x\K{*}\|p+{*}\|p+\\{carry};{}$\6 |
\&{if} ${}(\|x\G\T{1000000000}){}$\1\5 |
${}\\{carry}\K\T{1},\39{*}\|p\K\|x-\T{1000000000};{}$\2\6 |
\&{else}\1\5 |
${}\\{carry}\K\T{0},\39{*}\|p\K\|x;{}$\2\6 |
\4${}\}{}$\2\6 |
${}{*}\|p\K\\{carry};{}$\6 |
\&{if} (\\{carry})\1\5 |
${}\|f\MG\|a\MM;{}$\2\6 |
\&{if} ${}(\|f\MG\\{dat}[\|f\MG\|b]\E\T{0}\W\|f\MG\|b>\|f\MG\|a){}$\1\5 |
${}\|f\MG\|b\MM;{}$\2\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{83}\B\X83:Determine the binary fraction and binary exponent\X${}\E{}$\6 |
$\\{val}\K\\{zero\_octa};{}$\6 |
\&{if} ${}(\ff.\|a>\T{36}){}$\5 |
${}\{{}$\1\6 |
\&{for} ${}(\\{exp}\K\T{\^3fe};{}$ ${}\ff.\|a>\T{36};{}$ ${}\\{exp}\MM){}$\1\5 |
${}\\{bignum\_double}({\AND}\ff);{}$\2\6 |
\&{for} ${}(\|k\K\T{54};{}$ \|k; ${}\|k\MM){}$\5 |
${}\{{}$\1\6 |
\&{if} ${}(\ff.\\{dat}[\T{36}]){}$\5 |
${}\{{}$\1\6 |
\&{if} ${}(\|k\G\T{32}){}$\1\5 |
${}\\{val}.\|h\MRL{{\OR}{\K}}\T{1}\LL(\|k-\T{32}){}$;\5 |
\2\&{else}\1\5 |
${}\\{val}.\|l\MRL{{\OR}{\K}}\T{1}\LL\|k;{}$\2\6 |
${}\ff.\\{dat}[\T{36}]\K\T{0};{}$\6 |
\&{if} ${}(\ff.\|b\E\T{36}){}$\1\5 |
\&{break};\C{ break if \PB{$\ff$} now zero }\2\6 |
\4${}\}{}$\2\6 |
${}\\{bignum\_double}({\AND}\ff);{}$\6 |
\4${}\}{}$\2\6 |
\4${}\}{}$\5 |
\2\&{else}\5 |
${}\{{}$\1\6 |
${}\\{tt}.\|a\K\\{tt}.\|b\K\T{36},\39\\{tt}.\\{dat}[\T{36}]\K\T{2};{}$\6 |
\&{for} ${}(\\{exp}\K\T{\^3fe};{}$ ${}\\{bignum\_compare}({\AND}\ff,\39{\AND}% |
\\{tt})\G\T{0};{}$ ${}\\{exp}\PP){}$\1\5 |
${}\\{bignum\_double}({\AND}\\{tt});{}$\2\6 |
\&{for} ${}(\|k\K\T{54};{}$ \|k; ${}\|k\MM){}$\5 |
${}\{{}$\1\6 |
${}\\{bignum\_double}({\AND}\ff);{}$\6 |
\&{if} ${}(\\{bignum\_compare}({\AND}\ff,\39{\AND}\\{tt})\G\T{0}){}$\5 |
${}\{{}$\1\6 |
\&{if} ${}(\|k\G\T{32}){}$\1\5 |
${}\\{val}.\|h\MRL{{\OR}{\K}}\T{1}\LL(\|k-\T{32}){}$;\5 |
\2\&{else}\1\5 |
${}\\{val}.\|l\MRL{{\OR}{\K}}\T{1}\LL\|k;{}$\2\6 |
${}\\{bignum\_dec}({\AND}\ff,\39{\AND}\\{tt},\39\T{1000000000});{}$\6 |
\&{if} ${}(\ff.\|a\E\\{bignum\_prec}-\T{1}){}$\1\5 |
\&{break};\C{ break if \PB{$\ff$} now zero }\2\6 |
\4${}\}{}$\2\6 |
\4${}\}{}$\2\6 |
\4${}\}{}$\2\6 |
\&{if} ${}(\|k\E\T{0}){}$\1\5 |
${}\\{val}.\|l\MRL{{\OR}{\K}}\T{1}{}$;\C{ add sticky bit if \PB{$\ff$} nonzero |
}\2\par |
\U78.\fi |
|
\M{84}We need to be careful that the input `\.{NaN.999999999999999999999}' |
doesn't |
get rounded up; it is supposed to yield \Hex{7fffffffffffffff}. |
|
Although the input `\.{NaN.0}' is illegal, strictly speaking, we silently |
convert it to \Hex{7ff0000000000001}---a number that would be |
output as `\.{NaN.0000000000000002}'. |
|
\Y\B\4\X84:Pack and round the answer\X${}\E{}$\6 |
$\\{val}\K\\{fpack}(\\{val},\39\\{exp},\39\\{sign},\39\.{ROUND\_NEAR});{}$\6 |
\&{if} (\\{NaN})\5 |
${}\{{}$\1\6 |
\&{if} ${}((\\{val}.\|h\AND\T{\^7fffffff})\E\T{\^40000000}){}$\1\5 |
${}\\{val}.\|h\MRL{{\OR}{\K}}\T{\^7fffffff},\39\\{val}.\|l\K\T{\^ffffffff};{}$% |
\2\6 |
\&{else} \&{if} ${}((\\{val}.\|h\AND\T{\^7fffffff})\E\T{\^3ff00000}\W\R\\{val}.% |
\|l){}$\1\5 |
${}\\{val}.\|h\MRL{{\OR}{\K}}\T{\^40000000},\39\\{val}.\|l\K\T{1};{}$\2\6 |
\&{else}\1\5 |
${}\\{val}.\|h\MRL{{\OR}{\K}}\T{\^40000000};{}$\2\6 |
\4${}\}{}$\2\par |
\U78.\fi |
|
\N{1}{85}Floating point remainders. In this section we implement the remainder |
of the floating point operations---one of which happens to be the |
operation of taking the remainder. |
|
The easiest task remaining is to compare two floating point quantities. |
Routine \PB{\\{fcomp}} returns $-1$~if~$y<z$, 0~if~$y=z$, $+1$~if~$y>z$, and |
$+2$~if $y$ and~$z$ are unordered. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{int} \\{fcomp}\,\,${}\.{ARGS}((\&{octa},\39\&{octa})){}$;\5 |
\hbox{}\6{}\&{int} ${}\\{fcomp}(\|y,\39\|z){}$\1\1\6 |
\&{octa} \|y${},{}$ \|z;\2\2\6 |
${}\{{}$\1\6 |
\&{ftype} \\{yt}${},{}$ \\{zt};\6 |
\&{int} \\{ye}${},{}$ \\{ze};\6 |
\&{char} \\{ys}${},{}$ \\{zs};\6 |
\&{octa} \\{yf}${},{}$ \\{zf};\6 |
\&{register} \&{int} \|x;\7 |
${}\\{yt}\K\\{funpack}(\|y,\39{\AND}\\{yf},\39{\AND}\\{ye},\39{\AND}\\{ys});{}$% |
\6 |
${}\\{zt}\K\\{funpack}(\|z,\39{\AND}\\{zf},\39{\AND}\\{ze},\39{\AND}\\{zs});{}$% |
\6 |
\&{switch} ${}(\T{4}*\\{yt}+\\{zt}){}$\5 |
${}\{{}$\1\6 |
\4\&{case} \T{4}${}*\\{nan}+\\{nan}{}$:\5 |
\&{case} \T{4}${}*\\{zro}+\\{nan}{}$:\5 |
\&{case} \T{4}${}*\\{num}+\\{nan}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{nan}{}$:\5 |
\&{case} \T{4}${}*\\{nan}+\\{zro}{}$:\5 |
\&{case} \T{4}${}*\\{nan}+\\{num}{}$:\5 |
\&{case} \T{4}${}*\\{nan}+\\{inf}{}$:\5 |
\&{return} \T{2};\6 |
\4\&{case} \T{4}${}*\\{zro}+\\{zro}{}$:\5 |
\&{return} \T{0};\6 |
\4\&{case} \T{4}${}*\\{zro}+\\{num}{}$:\5 |
\&{case} \T{4}${}*\\{num}+\\{zro}{}$:\5 |
\&{case} \T{4}${}*\\{zro}+\\{inf}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{zro}{}$:\5 |
\&{case} \T{4}${}*\\{num}+\\{num}{}$:\5 |
\&{case} \T{4}${}*\\{num}+\\{inf}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{num}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{inf}{}$:\6 |
\&{if} ${}(\\{ys}\I\\{zs}){}$\1\5 |
${}\|x\K\T{1};{}$\2\6 |
\&{else} \&{if} ${}(\|y.\|h>\|z.\|h){}$\1\5 |
${}\|x\K\T{1};{}$\2\6 |
\&{else} \&{if} ${}(\|y.\|h<\|z.\|h){}$\1\5 |
${}\|x\K{-}\T{1};{}$\2\6 |
\&{else} \&{if} ${}(\|y.\|l>\|z.\|l){}$\1\5 |
${}\|x\K\T{1};{}$\2\6 |
\&{else} \&{if} ${}(\|y.\|l<\|z.\|l){}$\1\5 |
${}\|x\K{-}\T{1};{}$\2\6 |
\&{else}\1\5 |
\&{return} \T{0};\2\6 |
\&{break};\6 |
\4${}\}{}$\2\6 |
\&{return} ${}(\\{ys}\E\.{'-'}\?{-}\|x:\|x);{}$\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{86}Several \MMIX\ operations act on a single floating point number and |
accept an arbitrary rounding mode. For example, consider the |
operation of rounding to the nearest floating point integer: |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{fintegerize}\,\,${}\.{ARGS}((\&{octa},\39\&{int})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{fintegerize}(\|z,\39\|r){}$\1\1\6 |
\&{octa} \|z;\C{ the operand }\6 |
\&{int} \|r;\C{ the rounding mode }\2\2\6 |
${}\{{}$\1\6 |
\&{ftype} \\{zt};\6 |
\&{int} \\{ze};\6 |
\&{char} \\{zs};\6 |
\&{octa} \\{xf}${},{}$ \\{zf};\7 |
${}\\{zt}\K\\{funpack}(\|z,\39{\AND}\\{zf},\39{\AND}\\{ze},\39{\AND}\\{zs});{}$% |
\6 |
\&{if} ${}(\R\|r){}$\1\5 |
${}\|r\K\\{cur\_round};{}$\2\6 |
\&{switch} (\\{zt})\5 |
${}\{{}$\1\6 |
\4\&{case} \\{nan}:\5 |
\&{if} ${}(\R(\|z.\|h\AND\T{\^80000})){}$\5 |
${}\{{}$\5 |
\1${}\\{exceptions}\MRL{{\OR}{\K}}\.{I\_BIT}{}$;\5 |
${}\|z.\|h\MRL{{\OR}{\K}}\T{\^80000}{}$;\5 |
${}\}{}$\2\6 |
\4\&{case} \\{inf}:\5 |
\&{case} \\{zro}:\5 |
\&{return} \|z;\6 |
\4\&{case} \\{num}:\5 |
\X87:Integerize and \PB{\&{return}}\X;\6 |
\4${}\}{}$\2\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{87}\B\X87:Integerize and \PB{\&{return}}\X${}\E{}$\6 |
\&{if} ${}(\\{ze}\G\T{1074}){}$\1\5 |
\&{return} \\{fpack}${}(\\{zf},\39\\{ze},\39\\{zs},\39\.{ROUND\_OFF}){}$;\C{ |
already an integer }\2\6 |
\&{if} ${}(\\{ze}\Z\T{1020}){}$\1\5 |
${}\\{xf}.\|h\K\T{0},\39\\{xf}.\|l\K\T{1};{}$\2\6 |
\&{else}\5 |
${}\{{}$\5 |
\1\&{octa} \\{oo};\7 |
${}\\{xf}\K\\{shift\_right}(\\{zf},\39\T{1074}-\\{ze},\39\T{1});{}$\6 |
${}\\{oo}\K\\{shift\_left}(\\{xf},\39\T{1074}-\\{ze});{}$\6 |
\&{if} ${}(\\{oo}.\|l\I\\{zf}.\|l\V\\{oo}.\|h\I\\{zf}.\|h){}$\1\5 |
${}\\{xf}.\|l\MRL{{\OR}{\K}}\T{1}{}$;\C{ sticky bit }\2\6 |
\4${}\}{}$\2\6 |
\&{switch} (\|r)\5 |
${}\{{}$\1\6 |
\4\&{case} \.{ROUND\_DOWN}:\5 |
\&{if} ${}(\\{zs}\E\.{'-'}){}$\1\5 |
${}\\{xf}\K\\{incr}(\\{xf},\39\T{3}){}$;\5 |
\2\&{break};\6 |
\4\&{case} \.{ROUND\_UP}:\5 |
\&{if} ${}(\\{zs}\I\.{'-'}){}$\1\5 |
${}\\{xf}\K\\{incr}(\\{xf},\39\T{3});{}$\2\6 |
\4\&{case} \.{ROUND\_OFF}:\5 |
\&{break};\6 |
\4\&{case} \.{ROUND\_NEAR}:\5 |
${}\\{xf}\K\\{incr}(\\{xf},\39\\{xf}.\|l\AND\T{4}\?\T{2}:\T{1}){}$;\5 |
\&{break};\6 |
\4${}\}{}$\2\6 |
${}\\{xf}.\|l\MRL{\AND{\K}}\T{\^fffffffc};{}$\6 |
\&{if} ${}(\\{ze}\G\T{1022}){}$\1\5 |
\&{return} \\{fpack}${}(\\{shift\_left}(\\{xf},\39\T{1074}-\\{ze}),\39\\{ze},% |
\39\\{zs},\39\.{ROUND\_OFF});{}$\2\6 |
\&{if} ${}(\\{xf}.\|l){}$\1\5 |
${}\\{xf}.\|h\K\T{\^3ff00000},\39\\{xf}.\|l\K\T{0};{}$\2\6 |
\&{if} ${}(\\{zs}\E\.{'-'}){}$\1\5 |
${}\\{xf}.\|h\MRL{{\OR}{\K}}\\{sign\_bit};{}$\2\6 |
\&{return} \\{xf};\par |
\U86.\fi |
|
\M{88}To convert floating point to fixed point, we use \PB{\\{fixit}}. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{fixit}\,\,${}\.{ARGS}((\&{octa},\39\&{int})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{fixit}(\|z,\39\|r){}$\1\1\6 |
\&{octa} \|z;\C{ the operand }\6 |
\&{int} \|r;\C{ the rounding mode }\2\2\6 |
${}\{{}$\1\6 |
\&{ftype} \\{zt};\6 |
\&{int} \\{ze};\6 |
\&{char} \\{zs};\6 |
\&{octa} \\{zf}${},{}$ \|o;\7 |
${}\\{zt}\K\\{funpack}(\|z,\39{\AND}\\{zf},\39{\AND}\\{ze},\39{\AND}\\{zs});{}$% |
\6 |
\&{if} ${}(\R\|r){}$\1\5 |
${}\|r\K\\{cur\_round};{}$\2\6 |
\&{switch} (\\{zt})\5 |
${}\{{}$\1\6 |
\4\&{case} \\{nan}:\5 |
\&{case} \\{inf}:\5 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{I\_BIT}{}$;\5 |
\&{return} \|z;\6 |
\4\&{case} \\{zro}:\5 |
\&{return} \\{zero\_octa};\6 |
\4\&{case} \\{num}:\5 |
\&{if} ${}(\\{funpack}(\\{fintegerize}(\|z,\39\|r),\39{\AND}\\{zf},\39{\AND}% |
\\{ze},\39{\AND}\\{zs})\E\\{zro}){}$\1\5 |
\&{return} \\{zero\_octa};\2\6 |
\&{if} ${}(\\{ze}\Z\T{1076}){}$\1\5 |
${}\|o\K\\{shift\_right}(\\{zf},\39\T{1076}-\\{ze},\39\T{1});{}$\2\6 |
\&{else}\5 |
${}\{{}$\1\6 |
\&{if} ${}(\\{ze}>\T{1085}\V(\\{ze}\E\T{1085}\W(\\{zf}.\|h>\T{\^400000}\V% |
\3{-1}(\\{zf}.\|h\E\T{\^400000}\W(\\{zf}.\|l\V\\{zs}\I\.{'-'}))))){}$\1\5 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{W\_BIT};{}$\2\6 |
\&{if} ${}(\\{ze}\G\T{1140}){}$\1\5 |
\&{return} \\{zero\_octa};\2\6 |
${}\|o\K\\{shift\_left}(\\{zf},\39\\{ze}-\T{1076});{}$\6 |
\4${}\}{}$\2\6 |
\&{return} ${}(\\{zs}\E\.{'-'}\?\\{ominus}(\\{zero\_octa},\39\|o):\|o);{}$\6 |
\4${}\}{}$\2\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{89}Going the other way, we can specify not only a rounding mode but whether |
the given fixed point octabyte is signed or unsigned, and whether the |
result should be rounded to short precision. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{floatit}\,\,${}\.{ARGS}((\&{octa},\39\&{int},\39\&{int},\39% |
\&{int})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{floatit}(\|z,\39\|r,\39\|u,\39\|p){}$\1\1\6 |
\&{octa} \|z;\C{ octabyte to float }\6 |
\&{int} \|r;\C{ rounding mode }\6 |
\&{int} \|u;\C{ unsigned? }\6 |
\&{int} \|p;\C{ short precision? }\2\2\6 |
${}\{{}$\1\6 |
\&{int} \|e;\5 |
\&{char} \|s;\6 |
\&{register} \&{int} \|t;\7 |
${}\\{exceptions}\K\T{0};{}$\6 |
\&{if} ${}(\R\|z.\|h\W\R\|z.\|l){}$\1\5 |
\&{return} \\{zero\_octa};\2\6 |
\&{if} ${}(\R\|r){}$\1\5 |
${}\|r\K\\{cur\_round};{}$\2\6 |
\&{if} ${}(\R\|u\W(\|z.\|h\AND\\{sign\_bit})){}$\1\5 |
${}\|s\K\.{'-'},\39\|z\K\\{ominus}(\\{zero\_octa},\39\|z){}$;\5 |
\2\&{else}\1\5 |
${}\|s\K\.{'+'};{}$\2\6 |
${}\|e\K\T{1076};{}$\6 |
\&{while} ${}(\|z.\|h<\T{\^400000}){}$\1\5 |
${}\|e\MM,\39\|z\K\\{shift\_left}(\|z,\39\T{1});{}$\2\6 |
\&{while} ${}(\|z.\|h\G\T{\^800000}){}$\5 |
${}\{{}$\1\6 |
${}\|e\PP;{}$\6 |
${}\|t\K\|z.\|l\AND\T{1};{}$\6 |
${}\|z\K\\{shift\_right}(\|z,\39\T{1},\39\T{1});{}$\6 |
${}\|z.\|l\MRL{{\OR}{\K}}\|t;{}$\6 |
\4${}\}{}$\2\6 |
\&{if} (\|p)\1\5 |
\X90:Convert to short float\X;\2\6 |
\&{return} \\{fpack}${}(\|z,\39\|e,\39\|s,\39\|r);{}$\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{90}\B\X90:Convert to short float\X${}\E{}$\6 |
${}\{{}$\1\6 |
\&{register} \&{int} \\{ex};\5 |
\&{register} \&{tetra} \|t;\7 |
${}\|t\K\\{sfpack}(\|z,\39\|e,\39\|s,\39\|r);{}$\6 |
${}\\{ex}\K\\{exceptions};{}$\6 |
${}\\{sfunpack}(\|t,\39{\AND}\|z,\39{\AND}\|e,\39{\AND}\|s);{}$\6 |
${}\\{exceptions}\K\\{ex};{}$\6 |
\4${}\}{}$\2\par |
\U89.\fi |
|
\M{91}The square root operation is more interesting. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{froot}\,\,${}\.{ARGS}((\&{octa},\39\&{int})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{froot}(\|z,\39\|r){}$\1\1\6 |
\&{octa} \|z;\C{ the operand }\6 |
\&{int} \|r;\C{ the rounding mode }\2\2\6 |
${}\{{}$\1\6 |
\&{ftype} \\{zt};\6 |
\&{int} \\{ze};\6 |
\&{char} \\{zs};\6 |
\&{octa} \|x${},{}$ \\{xf}${},{}$ \\{rf}${},{}$ \\{zf};\6 |
\&{register} \&{int} \\{xe}${},{}$ \|k;\7 |
\&{if} ${}(\R\|r){}$\1\5 |
${}\|r\K\\{cur\_round};{}$\2\6 |
${}\\{zt}\K\\{funpack}(\|z,\39{\AND}\\{zf},\39{\AND}\\{ze},\39{\AND}\\{zs});{}$% |
\6 |
\&{if} ${}(\\{zs}\E\.{'-'}\W\\{zt}\I\\{zro}){}$\1\5 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{I\_BIT},\39\|x\K\\{standard\_NaN};{}$\2\6 |
\&{else}\5 |
\1\&{switch} (\\{zt})\5 |
${}\{{}$\1\6 |
\4\&{case} \\{nan}:\5 |
\&{if} ${}(\R(\|z.\|h\AND\T{\^80000})){}$\1\5 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{I\_BIT},\39\|z.\|h\MRL{{\OR}{\K}}\T{% |
\^80000};{}$\2\6 |
\&{return} \|z;\6 |
\4\&{case} \\{inf}:\5 |
\&{case} \\{zro}:\5 |
${}\|x\K\|z{}$;\5 |
\&{break};\6 |
\4\&{case} \\{num}:\5 |
\X92:Take the square root and \PB{\&{return}}\X;\6 |
\4${}\}{}$\2\2\6 |
\&{if} ${}(\\{zs}\E\.{'-'}){}$\1\5 |
${}\|x.\|h\MRL{{\OR}{\K}}\\{sign\_bit};{}$\2\6 |
\&{return} \|x;\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{92}The square root can be found by an adaptation of the old pencil-and-paper |
method. If $n=\lfloor\sqrt s\rfloor$, where $s$ is an integer, |
we have $s=n^2+r$ where $0\le r\le2n$; |
this invariant can be maintained if we replace $s$ by $4s+(0,1,2,3)$ |
and $n$ by $2n+(0,1)$. The following code implements this idea with |
$2n$ in~\PB{\\{xf}} and $r$ in~\PB{\\{rf}}. (It could easily be made to run |
about |
twice as fast.) |
|
\Y\B\4\X92:Take the square root and \PB{\&{return}}\X${}\E{}$\6 |
$\\{xf}.\|h\K\T{0},\39\\{xf}.\|l\K\T{2};{}$\6 |
${}\\{xe}\K(\\{ze}+\T{\^3fe})\GG\T{1};{}$\6 |
\&{if} ${}(\\{ze}\AND\T{1}){}$\1\5 |
${}\\{zf}\K\\{shift\_left}(\\{zf},\39\T{1});{}$\2\6 |
${}\\{rf}.\|h\K\T{0},\39\\{rf}.\|l\K(\\{zf}.\|h\GG\T{22})-\T{1};{}$\6 |
\&{for} ${}(\|k\K\T{53};{}$ \|k; ${}\|k\MM){}$\5 |
${}\{{}$\1\6 |
${}\\{rf}\K\\{shift\_left}(\\{rf},\39\T{2}){}$;\5 |
${}\\{xf}\K\\{shift\_left}(\\{xf},\39\T{1});{}$\6 |
\&{if} ${}(\|k\G\T{43}){}$\1\5 |
${}\\{rf}\K\\{incr}(\\{rf},\39(\\{zf}.\|h\GG(\T{2}*(\|k-\T{43})))\AND\T{3});{}$% |
\2\6 |
\&{else} \&{if} ${}(\|k\G\T{27}){}$\1\5 |
${}\\{rf}\K\\{incr}(\\{rf},\39(\\{zf}.\|l\GG(\T{2}*(\|k-\T{27})))\AND\T{3});{}$% |
\2\6 |
\&{if} ${}((\\{rf}.\|l>\\{xf}.\|l\W\\{rf}.\|h\G\\{xf}.\|h)\V\\{rf}.\|h>\\{xf}.% |
\|h){}$\5 |
${}\{{}$\1\6 |
${}\\{xf}.\|l\PP{}$;\5 |
${}\\{rf}\K\\{ominus}(\\{rf},\39\\{xf}){}$;\5 |
${}\\{xf}.\|l\PP;{}$\6 |
\4${}\}{}$\2\6 |
\4${}\}{}$\2\6 |
\&{if} ${}(\\{rf}.\|h\V\\{rf}.\|l){}$\1\5 |
${}\\{xf}.\|l\PP{}$;\C{ sticky bit }\2\6 |
\&{return} \\{fpack}${}(\\{xf},\39\\{xe},\39\.{'+'},\39\|r){}$;\par |
\U91.\fi |
|
\M{93}And finally, the genuine floating point remainder. Subroutine \PB{% |
\\{fremstep}} |
either calculates $y\,{\rm rem}\,z$ or reduces $y$ to a smaller number |
having the same remainder with respect to~$z$. In the latter case |
the \PB{\.{E\_BIT}} is set in \PB{\\{exceptions}}. A third parameter, \PB{% |
\\{delta}}, |
gives a decrease in exponent that is acceptable for incomplete results; |
if \PB{\\{delta}} is sufficiently large, say 2500, the correct result will |
always be obtained in one step of \PB{\\{fremstep}}. |
|
\Y\B\4\X5:Subroutines\X${}\mathrel+\E{}$\6 |
\&{octa} \\{fremstep}\,\,${}\.{ARGS}((\&{octa},\39\&{octa},\39\&{int})){}$;\5 |
\hbox{}\6{}\&{octa} ${}\\{fremstep}(\|y,\39\|z,\39\\{delta}){}$\1\1\6 |
\&{octa} \|y${},{}$ \|z;\6 |
\&{int} \\{delta};\2\2\6 |
${}\{{}$\1\6 |
\&{ftype} \\{yt}${},{}$ \\{zt};\6 |
\&{int} \\{ye}${},{}$ \\{ze};\6 |
\&{char} \\{xs}${},{}$ \\{ys}${},{}$ \\{zs};\6 |
\&{octa} \|x${},{}$ \\{xf}${},{}$ \\{yf}${},{}$ \\{zf};\6 |
\&{register} \&{int} \\{xe}${},{}$ \\{thresh}${},{}$ \\{odd};\7 |
${}\\{yt}\K\\{funpack}(\|y,\39{\AND}\\{yf},\39{\AND}\\{ye},\39{\AND}\\{ys});{}$% |
\6 |
${}\\{zt}\K\\{funpack}(\|z,\39{\AND}\\{zf},\39{\AND}\\{ze},\39{\AND}\\{zs});{}$% |
\6 |
\&{switch} ${}(\T{4}*\\{yt}+\\{zt}){}$\5 |
${}\{{}$\1\6 |
\hbox{\4}\X42:The usual NaN cases\X;\6 |
\4\&{case} \T{4}${}*\\{zro}+\\{zro}{}$:\5 |
\&{case} \T{4}${}*\\{num}+\\{zro}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{zro}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{num}{}$:\5 |
\&{case} \T{4}${}*\\{inf}+\\{inf}{}$:\5 |
${}\|x\K\\{standard\_NaN};{}$\6 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{I\_BIT}{}$;\5 |
\&{break};\6 |
\4\&{case} \T{4}${}*\\{zro}+\\{num}{}$:\5 |
\&{case} \T{4}${}*\\{zro}+\\{inf}{}$:\5 |
\&{case} \T{4}${}*\\{num}+\\{inf}{}$:\5 |
\&{return} \|y;\6 |
\4\&{case} \T{4}${}*\\{num}+\\{num}{}$:\5 |
\X94:Remainderize nonzero numbers and \PB{\&{return}}\X;\6 |
\4\\{zero\_out}:\5 |
${}\|x\K\\{zero\_octa};{}$\6 |
\4${}\}{}$\2\6 |
\&{if} ${}(\\{ys}\E\.{'-'}){}$\1\5 |
${}\|x.\|h\MRL{{\OR}{\K}}\\{sign\_bit};{}$\2\6 |
\&{return} \|x;\6 |
\4${}\}{}$\2\par |
\fi |
|
\M{94}If there's a huge difference in exponents and the remainder is nonzero, |
this computation will take a long time. One could compute |
$(2^ny)\,{\rm rem}\,z$ much more quickly for large~$n$ by using $O(\log n)$ |
multiplications modulo~$z$, but the floating remainder operation isn't |
important enough to justify such expensive hardware. |
|
Results of floating remainder are always exact, so the rounding mode |
is immaterial. |
|
\Y\B\4\X94:Remainderize nonzero numbers and \PB{\&{return}}\X${}\E{}$\6 |
$\\{odd}\K\T{0}{}$;\C{ will be 1 if we've subtracted an odd multiple of~$z$ |
from $y$ }\6 |
${}\\{thresh}\K\\{ye}-\\{delta};{}$\6 |
\&{if} ${}(\\{thresh}<\\{ze}){}$\1\5 |
${}\\{thresh}\K\\{ze};{}$\2\6 |
\&{while} ${}(\\{ye}\G\\{thresh}){}$\1\5 |
\X95:Reduce \PB{$(\\{ye},\\{yf})$} by a multiple of \PB{\\{zf}}; \PB{\&{goto} % |
\\{zero\_out}} if the remainder is zero, \PB{\&{goto} \\{try\_complement}} if |
appropriate\X;\2\6 |
\&{if} ${}(\\{ye}\G\\{ze}){}$\5 |
${}\{{}$\1\6 |
${}\\{exceptions}\MRL{{\OR}{\K}}\.{E\_BIT}{}$;\5 |
\&{return} \\{fpack}${}(\\{yf},\39\\{ye},\39\\{ys},\39\.{ROUND\_OFF});{}$\6 |
\4${}\}{}$\2\6 |
\&{if} ${}(\\{ye}<\\{ze}-\T{1}){}$\1\5 |
\&{return} \\{fpack}${}(\\{yf},\39\\{ye},\39\\{ys},\39\.{ROUND\_OFF});{}$\2\6 |
${}\\{yf}\K\\{shift\_right}(\\{yf},\39\T{1},\39\T{1});{}$\6 |
\4\\{try\_complement}:\5 |
${}\\{xf}\K\\{ominus}(\\{zf},\39\\{yf}),\39\\{xe}\K\\{ze},\39\\{xs}\K\.{'+'}+% |
\.{'-'}-\\{ys};{}$\6 |
\&{if} ${}(\\{xf}.\|h>\\{yf}.\|h\V(\\{xf}.\|h\E\\{yf}.\|h\W(\\{xf}.\|l>\\{yf}.% |
\|l\V(\\{xf}.\|l\E\\{yf}.\|l\W\R\\{odd})))){}$\1\5 |
${}\\{xf}\K\\{yf},\39\\{xs}\K\\{ys};{}$\2\6 |
\&{while} ${}(\\{xf}.\|h<\T{\^400000}){}$\1\5 |
${}\\{xe}\MM,\39\\{xf}\K\\{shift\_left}(\\{xf},\39\T{1});{}$\2\6 |
\&{return} \\{fpack}${}(\\{xf},\39\\{xe},\39\\{xs},\39\.{ROUND\_OFF}){}$;\par |
\U93.\fi |
|
\M{95}Here we are careful not to change the sign of \PB{\|y}, because a |
remainder |
of~0 is supposed to inherit the original sign of~\PB{\|y}. |
|
\Y\B\4\X95:Reduce \PB{$(\\{ye},\\{yf})$} by a multiple of \PB{\\{zf}}; \PB{% |
\&{goto} \\{zero\_out}} if the remainder is zero, \PB{\&{goto} \\{try% |
\_complement}} if appropriate\X${}\E{}$\6 |
${}\{{}$\1\6 |
\&{if} ${}(\\{yf}.\|h\E\\{zf}.\|h\W\\{yf}.\|l\E\\{zf}.\|l){}$\1\5 |
\&{goto} \\{zero\_out};\2\6 |
\&{if} ${}(\\{yf}.\|h<\\{zf}.\|h\V(\\{yf}.\|h\E\\{zf}.\|h\W\\{yf}.\|l<\\{zf}.% |
\|l)){}$\5 |
${}\{{}$\1\6 |
\&{if} ${}(\\{ye}\E\\{ze}){}$\1\5 |
\&{goto} \\{try\_complement};\2\6 |
${}\\{ye}\MM,\39\\{yf}\K\\{shift\_left}(\\{yf},\39\T{1});{}$\6 |
\4${}\}{}$\2\6 |
${}\\{yf}\K\\{ominus}(\\{yf},\39\\{zf});{}$\6 |
\&{if} ${}(\\{ye}\E\\{ze}){}$\1\5 |
${}\\{odd}\K\T{1};{}$\2\6 |
\&{while} ${}(\\{yf}.\|h<\T{\^400000}){}$\1\5 |
${}\\{ye}\MM,\39\\{yf}\K\\{shift\_left}(\\{yf},\39\T{1});{}$\2\6 |
\4${}\}{}$\2\par |
\U94.\fi |
|
\N{1}{96}Index. |
|
\fi |
|
|
\inx |
\fin |
\con |