/* $OpenBSD: ieetst.c,v 1.2 2011/07/03 13:39:07 martynas Exp $ */ /* * Copyright (c) 2008 Stephen L. Moshier * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Floating point to ASCII input and output string test program. * * Numbers in the native machine data structure are converted * to e type, then to and from decimal ASCII strings. Native * printf() and scanf() functions are also used to produce * and read strings. The resulting e type binary values * are compared, with diagnostic printouts of any discrepancies. * * Steve Moshier, 16 Dec 88 * last revision: 16 May 92 */ #include #include #include "mconf.h" #include "ehead.h" /* Include tests of 80-bit long double precision: */ #if LDBL_MANT_DIG == 64 #define LDOUBLE 1 #else /* LDBL_MANT_DIG == 64 */ #define LDOUBLE 0 #endif /* LDBL_MANT_DIG == 64 */ /* Abort subtest after getting this many errors: */ #define MAXERR 5 /* Number of random arguments to try (set as large as you have * patience for): */ #define NRAND 100 /* Perform internal consistency test: */ #define CHKINTERNAL 0 static unsigned short fullp[NE], rounded[NE]; float prec24, sprec24, ssprec24; double prec53, sprec53, ssprec53; #if LDOUBLE long double prec64, sprec64, ssprec64; #endif static unsigned short rprint[NE], rscan[NE]; static unsigned short q1[NE], q2[NE], q5[NE]; static unsigned short e1[NE], e2[NE], e3[NE]; static double d1, d2; static int errprint = 0; static int errscan = 0; static int identerr = 0; static int errtot = 0; static int count = 0; static char str0[80], str1[80], str2[80], str3[80]; static unsigned short eten[NE], maxm[NE]; int m, n, k2, mprec, SPREC; char *Ten = "10.0"; char tformat[10]; char *format24 = "%.8e"; #ifdef DEC char *format53 = "%.17e"; #else char *format53 = "%.16e"; #endif char *fformat24 = "%e"; char *fformat53 = "%le"; char *pct = "%"; char *quo = "\042"; #if LDOUBLE char *format64 = "%.20Le"; char *fformat64 = "%Le"; #endif char *format; char *fformat; char *toomany = "Too many errors; aborting this test.\n"; static int mnrflag; static int etrflag; void chkit(), printerr(), mnrand(), etrand(), shownoncrit(); void chkid(), pvec(); main() { int i, iprec, retval = 0; printf( "Steve Moshier's printf/scanf tester, version 0.2.\n\n" ); #ifdef DEC /* DEC PDP-11/VAX single precision not yet implemented */ for( iprec = 1; iprec<2; iprec++ ) #else for( iprec = 0; iprec<3; iprec++ ) /*for( iprec = 2; iprec<3; iprec++ )*/ #endif { errscan = 0; identerr = 0; errprint = 0; eclear( rprint ); eclear( rscan ); switch( iprec ) { case 0: SPREC = 8; /* # digits after the decimal point */ mprec = 24; /* # bits in the significand */ m = 9; /* max # decimal digits for correct rounding */ n = 13; /* max power of ten for correct rounding */ k2 = -125; /* underflow beyond 2^-k2 */ format = format24; /* printf format string */ fformat = fformat24; /* scanf format string */ mnrflag = 1; /* sets interval for random numbers */ etrflag = 1; printf( "Testing FLOAT precision.\n" ); break; case 1: #ifdef DEC SPREC = 17; mprec = 56; m = 17; n = 27; k2 = -125; format = format53; fformat = fformat53; mnrflag = 2; etrflag = 1; printf( "Testing DEC DOUBLE precision.\n" ); break; #else SPREC = 16; mprec = 53; m = 17; n = 27; k2 = -1021; format = format53; fformat = fformat53; mnrflag = 2; etrflag = 2; printf( "Testing DOUBLE precision.\n" ); break; #endif case 2: #if LDOUBLE SPREC = 20; mprec = 64; m = 20; n = 34; k2 = -16382; format = format64; fformat = fformat64; mnrflag = 3; etrflag = 3; printf( "Testing LONG DOUBLE precision.\n" ); break; #else goto nodenorm; #endif } asctoe( Ten, eten ); /* 10^m - 1 */ d2 = m; e53toe( &d2, e1 ); epow( eten, e1, maxm ); esub( eone, maxm, maxm ); /* test 1 */ printf( "1. Checking 10^n - 1 for n = %d to %d.\n", -m, m ); emov( eone, q5 ); for( count=0; count<=m; count++ ) { esub( eone, q5, fullp ); chkit( 1 ); ediv( q5, eone, q2 ); esub( eone, q2, fullp ); chkit( 1 ); emul( eten, q5, q5 ); if( errtot >= MAXERR ) { printf( "%s", toomany ); goto end1; } } end1: printerr(); /* test 2 */ printf( "2. Checking powers of 10 from 10^-%d to 10^%d.\n", n, n ); emov( eone, q5 ); for( count=0; count<=n; count++ ) { emov( q5, fullp ); chkit( 2 ); ediv( q5, eone, fullp ); chkit( 2 ); emul( eten, q5, q5 ); if( errtot >= MAXERR ) { printf( "%s", toomany ); goto end2; } } end2: printerr(); /* test 3 */ printf( "3. Checking (10^%d-1)*10^n from n = -%d to %d.\n", m, n, n ); emov( eone, q5 ); for( count= -n; count<=n; count++ ) { emul( maxm, q5, fullp ); chkit( 3 ); emov( q5, fullp ); ediv( fullp, eone, fullp ); emul( maxm, fullp, fullp ); chkit( 3 ); emul( eten, q5, q5 ); if( errtot >= MAXERR ) { printf( "%s", toomany ); goto end3; } } end3: printerr(); /* test 4 */ printf( "4. Checking powers of 2 from 2^-24 to 2^+56.\n" ); d1 = -24.0; e53toe( &d1, q1 ); epow( etwo, q1, q5 ); for( count = -24; count <= 56; count++ ) { emov( q5, fullp ); chkit( 4 ); emul( etwo, q5, q5 ); if( errtot >= MAXERR ) { printf( "%s", toomany ); goto end4; } } end4: printerr(); /* test 5 */ printf( "5. Checking 2^n - 1 for n = 0 to %d.\n", mprec ); emov( eone, q5 ); for( count=0; count<=mprec; count++ ) { esub( eone, q5, fullp ); chkit( 5 ); emul( etwo, q5, q5 ); if( errtot >= MAXERR ) { printf( "%s", toomany ); goto end5; } } end5: printerr(); /* test 6 */ printf( "6. Checking 2^n + 1 for n = 0 to %d.\n", mprec ); emov( eone, q5 ); for( count=0; count<=mprec; count++ ) { eadd( eone, q5, fullp ); chkit( 6 ); emul( etwo, q5, q5 ); if( errtot >= MAXERR ) { printf( "%s", toomany ); goto end6; } } end6: printerr(); /* test 7 */ printf( "7. Checking %d values M * 10^N with random integer M and N,\n", NRAND ); printf(" 1 <= M <= 10^%d - 1 and -%d <= N <= +%d.\n", m, n, n ); for( i=0; i= MAXERR ) { printf( "%s", toomany ); goto end7; } } end7: printerr(); /* test 8 */ printf("8. Checking critical rounding cases.\n" ); for( i=0; i<20; i++ ) { mnrand( fullp ); eabs( fullp ); if( ecmp( fullp, eone ) < 0 ) ediv( fullp, eone, fullp ); efloor( fullp, fullp ); eadd( ehalf, fullp, fullp ); chkit( 8 ); if( errtot >= MAXERR ) { printf( "%s", toomany ); goto end8; } } end8: printerr(); /* test 9 */ printf("9. Testing on %d random non-denormal values.\n", NRAND ); for( i=0; i= MAXERR ) { printf( "%s", toomany ); goto maxden; } ediv( etwo, e2, e2 ); } maxden: printerr(); nodenorm: printf( "\n" ); retval |= errscan | identerr | errprint; } /* loop on precision */ printf( "End of test.\n" ); return (retval); } #if CHKINTERNAL long double xprec64; double xprec53; float xprec24; /* Check binary -> printf -> scanf -> binary identity * of internal routines */ void chkinternal( ref, tst, string ) unsigned short ref[], tst[]; char *string; { if( ecmp(ref,tst) != 0 ) { printf( "internal identity compare error!\n" ); chkid( ref, tst, string ); } } #endif /* Check binary -> printf -> scanf -> binary identity */ void chkid( print, scan, string ) unsigned short print[], scan[]; char *string; { /* Test printf-scanf identity */ if( ecmp( print, scan ) != 0 ) { pvec( print, NE ); printf( " ->printf-> %s ->scanf->\n", string ); pvec( scan, NE ); printf( " is not an identity.\n" ); ++identerr; } } /* Check scanf result */ void chkscan( ref, tst, string ) unsigned short ref[], tst[]; char *string; { /* Test scanf() */ if( ecmp( ref, tst ) != 0 ) { printf( "scanf(%s) -> ", string ); pvec( tst, NE ); printf( "\n should be " ); pvec( ref, NE ); printf( ".\n" ); ++errscan; ++errtot; } } /* Test printf() result */ void chkprint( ref, tst, string ) unsigned short ref[], tst[]; char *string; { if( ecmp(ref, tst) != 0 ) { printf( "printf( "); pvec( ref, NE ); printf( ") -> %s\n", string ); printf( " = " ); pvec( tst, NE ); printf( ".\n" ); ++errprint; ++errtot; } } /* Print array of n 16-bit shorts */ void pvec( x, n ) unsigned short x[]; int n; { int i; for( i=0; i 0 ) emov( e, rprint ); } } /* Measure worst case scanf rounding error */ void cmpscan( ref, tst ) unsigned short ref[], tst[]; { unsigned short er[NE]; if( ecmp( ref, ezero ) != 0 ) { esub( ref, tst, er ); ediv( ref, er, er ); eabs( er ); if( ecmp( er, rscan ) > 0 ) emov( er, rscan ); if( ecmp( er, ehalf ) > 0 ) { etoasc( tst, str1, 21 ); printf( "Bad error: scanf(%s) = %s !\n", str0, str1 ); } } } /* Check rounded-down decimal string output of printf */ void cmptrunc( ref, tst ) unsigned short ref[], tst[]; { if( ecmp( ref, tst ) != 0 ) { printf( "printf(%s%s%s, %s) -> %s\n", quo, tformat, quo, str1, str2 ); printf( "should be %s .\n", str3 ); errprint += 1; } } void shownoncrit() { etoasc( rprint, str0, 3 ); printf( "Maximum relative printf error found = %s .\n", str0 ); etoasc( rscan, str0, 3 ); printf( "Maximum relative scanf error found = %s .\n", str0 ); } /* Produce arguments and call comparison subroutines. */ void chkit( testno ) int testno; { unsigned short t[NE], u[NE], v[NE]; int j; switch( mprec ) { #if LDOUBLE case 64: etoe64( fullp, &prec64 ); e64toe( &prec64, rounded ); #if CHKINTERNAL e64toasc( &prec64, str1, SPREC ); asctoe64( str1, &xprec64 ); e64toe( &xprec64, t ); chkinternal( rounded, t, str1 ); #endif /* check printf and scanf */ sprintf( str2, format, prec64 ); sscanf( str2, fformat, &sprec64 ); e64toe( &sprec64, u ); chkid( rounded, u, str2 ); asctoe64( str2, &ssprec64 ); e64toe( &ssprec64, v ); chkscan( v, u, str2 ); chkprint( rounded, v, str2 ); if( testno < 8 ) break; /* rounding error measurement */ etoasc( fullp, str0, 24 ); etoe64( fullp, &ssprec64 ); e64toe( &ssprec64, u ); sprintf( str2, format, ssprec64 ); asctoe( str2, t ); cmpprint( u, t ); sscanf( str0, fformat, &sprec64 ); e64toe( &sprec64, t ); cmpscan( fullp, t ); if( testno < 8 ) break; /* strings rounded to less than maximum precision */ e64toasc( &ssprec64, str1, 24 ); for( j=SPREC-1; j>0; j-- ) { e64toasc( &ssprec64, str3, j ); asctoe( str3, v ); sprintf( tformat, "%s.%dLe", pct, j ); sprintf( str2, tformat, ssprec64 ); asctoe( str2, t ); cmptrunc( v, t ); } break; #endif #ifdef DEC case 56: #endif case 53: etoe53( fullp, &prec53 ); e53toe( &prec53, rounded ); #if CHKINTERNAL e53toasc( &prec53, str1, SPREC ); asctoe53( str1, &xprec53 ); e53toe( &xprec53, t ); chkinternal( rounded, t, str1 ); #endif sprintf( str2, format, prec53 ); sscanf( str2, fformat, &sprec53 ); e53toe( &sprec53, u ); chkid( rounded, u, str2 ); asctoe53( str2, &ssprec53 ); e53toe( &ssprec53, v ); chkscan( v, u, str2 ); chkprint( rounded, v, str2 ); if( testno < 8 ) break; /* rounding error measurement */ etoasc( fullp, str0, 24 ); etoe53( fullp, &ssprec53 ); e53toe( &ssprec53, u ); sprintf( str2, format, ssprec53 ); asctoe( str2, t ); cmpprint( u, t ); sscanf( str0, fformat, &sprec53 ); e53toe( &sprec53, t ); cmpscan( fullp, t ); if( testno < 8 ) break; e53toasc( &ssprec53, str1, 24 ); for( j=SPREC-1; j>0; j-- ) { e53toasc( &ssprec53, str3, j ); asctoe( str3, v ); sprintf( tformat, "%s.%de", pct, j ); sprintf( str2, tformat, ssprec53 ); asctoe( str2, t ); cmptrunc( v, t ); } break; case 24: etoe24( fullp, &prec24 ); e24toe( &prec24, rounded ); #if CHKINTERNAL e24toasc( &prec24, str1, SPREC ); asctoe24( str1, &xprec24 ); e24toe( &xprec24, t ); chkinternal( rounded, t, str1 ); #endif sprintf( str2, format, prec24 ); sscanf( str2, fformat, &sprec24 ); e24toe( &sprec24, u ); chkid( rounded, u, str2 ); asctoe24( str2, &ssprec24 ); e24toe( &ssprec24, v ); chkscan( v, u, str2 ); chkprint( rounded, v, str2 ); if( testno < 8 ) break; /* rounding error measurement */ etoasc( fullp, str0, 24 ); etoe24( fullp, &ssprec24 ); e24toe( &ssprec24, u ); sprintf( str2, format, ssprec24 ); asctoe( str2, t ); cmpprint( u, t ); sscanf( str0, fformat, &sprec24 ); e24toe( &sprec24, t ); cmpscan( fullp, t ); /* if( testno < 8 ) break; */ e24toasc( &ssprec24, str1, 24 ); for( j=SPREC-1; j>0; j-- ) { e24toasc( &ssprec24, str3, j ); asctoe( str3, v ); sprintf( tformat, "%s.%de", pct, j ); sprintf( str2, tformat, ssprec24 ); asctoe( str2, t ); cmptrunc( v, t ); } break; } } void printerr() { if( (errscan == 0) && (identerr == 0) && (errprint == 0) ) printf( "No errors found.\n" ); else { printf( "%d binary -> decimal errors found.\n", errprint ); printf( "%d decimal -> binary errors found.\n", errscan ); } errscan = 0; /* reset for next test */ identerr = 0; errprint = 0; errtot = 0; } /* Random number generator * in the range M * 10^N, where 1 <= M <= 10^17 - 1 * and -27 <= N <= +27. Test values of M are logarithmically distributed * random integers; test values of N are uniformly distributed random integers. */ static char *fwidth = "1.036163291797320557783096e1"; /* log(sqrt(10^9-1)) */ static char *dwidth = "1.957197329044938830915E1"; /* log(sqrt(10^17-1)) */ static char *ldwidth = "2.302585092994045684017491e1"; /* log(sqrt(10^20-1)) */ static char *a13 = "13.0"; static char *a27 = "27.0"; static char *a34 = "34.0"; static char *a10m13 = "1.0e-13"; static unsigned short LOW[ NE ], WIDTH[NE], e27[NE], e10m13[NE]; void mnrand( erand ) unsigned short erand[]; { unsigned short ea[NE], em[NE], en[NE], ex[NE]; double x, a; if( mnrflag ) { if( mnrflag == 3 ) { asctoe( ldwidth, WIDTH ); asctoe( a34, e27 ); } if( mnrflag == 2 ) { asctoe( dwidth, WIDTH ); asctoe( a27, e27 ); } if( mnrflag == 1 ) { asctoe( fwidth, WIDTH ); asctoe( a13, e27 ); } asctoe( a10m13, e10m13 ); mnrflag = 0; } drand( &x ); e53toe( &x, ex ); /* x = WIDTH * ( x - 1.0 ) + LOW; */ esub( eone, ex, ex ); emul( WIDTH, ex, ex ); eexp( ex, ex ); /* x = exp(x); */ drand( &a ); e53toe( &a, ea ); emul( ea, ex, ea ); /* a = 1.0e-13 * x * a; */ emul( e10m13, ea, ea ); eabs( ea ); eadd( ea, ex, ex ); /* add fuzz */ emul( ex, ex, ex ); /* square it, to get range to 10^17 - 1 */ efloor( ex, em ); /* this is M */ /* Random power of 10 */ drand( &a ); e53toe( &a, ex ); esub( eone, ex, ex ); /* y3 = 54.0 * ( y3 - 1.0 ) + 0.5; */ emul( e27, ex, ex ); eadd( ex, ex, ex ); eadd( ehalf, ex, ex ); efloor( ex, ex ); /* y3 = floor( y3 ) - 27.0; */ esub( e27, ex, en ); /* this is N */ epow( eten, en, ex ); emul( ex, em, erand ); } /* -ln 2^16382 */ char *ldemin = "-1.1355137111933024058873097E4"; char *ldewid = "2.2710274223866048117746193E4"; /* -ln 2^1022 */ char *demin = "-7.0839641853226410622441123E2"; char *dewid = "1.4167928370645282124488225E3"; /* -ln 2^125 */ char *femin = "-8.6643397569993163677154015E1"; char *fewid = "1.7328679513998632735430803E2"; void etrand( erand ) unsigned short erand[]; { unsigned short ea[NE], ex[NE]; double x, a; if( etrflag ) { if( etrflag == 3 ) { asctoe( ldemin, LOW ); asctoe( ldewid, WIDTH ); asctoe( a34, e27 ); } if( etrflag == 2 ) { asctoe( demin, LOW ); asctoe( dewid, WIDTH ); asctoe( a27, e27 ); } if( etrflag == 1 ) { asctoe( femin, LOW ); asctoe( fewid, WIDTH ); asctoe( a13, e27 ); } asctoe( a10m13, e10m13 ); etrflag = 0; } drand( &x ); e53toe( &x, ex ); /* x = WIDTH * ( x - 1.0 ) + LOW; */ esub( eone, ex, ex ); emul( WIDTH, ex, ex ); eadd( LOW, ex, ex ); eexp( ex, ex ); /* x = exp(x); */ /* add fuzz */ drand( &a ); e53toe( &a, ea ); emul( ea, ex, ea ); /* a = 1.0e-13 * x * a; */ emul( e10m13, ea, ea ); if( ecmp( ex, ezero ) > 0 ) eneg( ea ); eadd( ea, ex, erand ); }