/* * Classified Tydeman Consulting Proprietary * Copyright (C) 1996, 2008 Tydeman Consulting. All rights reserved. * * Fred Tydeman tydeman@tybor.com * * Any person is hereby authorized to copy, use, and distribute, this * one sample test, subject to the following conditions: * * 1. the software may not be redistributed for a fee except as * reasonable to cover media costs; * 2. any copy of the software must include this notice, as well as * any other embedded copyright notices; and * 3. any distribution of this software or derivative works thereof * must comply with all applicable U.S. export control laws. * * Floating-Point C Extensions (FPCE) have been added to C99 (the * revision of the C language finished in late 1999). If you are * interested in other sample tests of the FPCE Test Suite, which * tests the floating-point and numerics capabilities of C/C++ * compilers and libraries, please see the FTP site: * ftp://ftp.tybor.com * user ID: anonymous * password: your email address * ***************************************************************** * * Does the C/C++ compiler/library correctly round a number printed * via %f? * * This is a test of rounding of %f via sprintf, using %e as the basis * to determine the expected result. * * These functions test sprintf() to see if it correctly converts from * internal (binary) to external (decimal) representation of floating- * point numbers. This tests compliance to the former Standard C, * also known as C89 (ANSI), C90 (ISO), or C95 (amendment one) and of the * current Standard C, also known as C99 (or C9X as it was being done). * * Assertion being tested: ISO/ANSI 9899-1990: * * 7.9.6.1 The fprintf function: Under 'f' and under 'e,E': * * "The value is rounded to the appropriate number of digits." * * And, under Environmental limit (in the same section): * * "The minimum value for the maximum number of characters produced by * any single conversion shall be 509." C99 raised this to 4095. * * Value->Rounded Format Values tested -> Rounded * .20 .2 %.0f 1.1 ... 9.4 1. ... 9. * .25 .2 or .3 %.1f .11 ... .94 0.1 ... 0.9 * .30 .3 %.2f .011 ... .094 0.01 ... 0.09 * .35 .3 or .4 %.3f .0011 ... .0094 0.001 ... 0.009 * * * Hints on using this test: * * 1) Compile as is. The only known compile time failure is incorrect * support for array initializers. If that is the cause, change the * #if 1 to #if 0 in the function stdc_failures(). If there is another * compile time failure, please send the results to Tydeman to see if * a workaround can be found. * * 2) Run as is. Please feedback these results to tydeman@tybor.com * * On a 33 Mhz Intel 80486DX, this test takes between 30 and 80 seconds * (depends upon compiler). I have been told that another system took * 5 minutes (but it also had no failures). * * 3) If there are runtime failures and you want to find out why: * * a) Try defining USE_Le (just after these comments) and running * to see if using %Le in place of %e gives fewer failures (This * bypass will only work if long double has more precision than double * and %Le prints more correct digits than %e). If using %Le in place * of %e gives fewer failures, then %e is most likely rounding to fewer * than the user specified number of digits. This matters for numbers * such as: 4.5e-13, 5.5e-17, 5.5e-18, 1.5e-34, 1.5e-37, 3.5e-39 which * are very close to X.4999...9YYY (or X.5000...0YYY) and are being * incorrectly rounded to exact midpoint X.5000... This test program * computes the expected %f result from the %e result. If the %e result * is wrong, then the expected %f computed may be wrong and a correct %f * result from printf() may be reported as an error. Note: The only * numbers that should print as X.5000... from %e are X.5eNN, where X * is any integer, including 0, and NN is either 0 or -1; for NN = -2, * -3, -4, ..., the values are not exactly X.5eNN when converted from * binary to decimal. * * A specific example of this is these two numbers: * * Decimal -> IEEE-754 double -> 17 digit decimal plus more * 3.5e-4 = 3F36F0068DB8BAC7 = 3.4999999999999999 6443816874247545e-4 * 8.5e-9 = 3E4240ECA6A943FE = 8.5000000000000000 1240564454548648e-9 * * If %e conversion code rounds the decimal string to 17 digits * (instead of the requested 99), then this program sees 3.50...0e-4 * and 8.50...0e-9 which are taken to be exact midpoints. * * If you wish to not print the x.50...0e-nn failures, then define * SUPPRESS_MIDPOINT (just after these comments). * * b) Try changing the value of MIN_WIDTH (just after these comments) * to a smaller value. If that gives fewer failures, then %f is not * printing all the digits being asked for (Some compilers limit the * number of digits that are printed to less than 509 (4095 in C99); * such as 32, 42, or 64, three values seen so far). * * * Results of running this test on various compilers: * (as reported by several users of this test; your mileage may vary) * * Failures Hardware Operating system Compiler, version, and options * 0 ARM + FPU RiscOS Norcroft ARM v4.69 C [no options] * 0 ARM + SWFP RiscOS Norcroft ARM v4.69 C [no options] * 0 Apple Mac PPC 603e Tenon MachTen gcc 2.7.2.1 * 0 Apple Mac PPC G4 Mac OS X 10.3.7 Gnu gcc 3.3 (build 1495) * 0 Apple Mac PPC G4 Mac OS X 10.3.7 IBM XL C 6.0 + Gnu gcc 3.3 * 0 DEC Alpha Windows NT 4.0 SP4 Visual C++ 5.0 * 0 Intel 80486/66 Windows 95 IBM Visual Age C/C++ V3.5 * 0 Intel 80486/66 Windows 95 Microsoft C/C++ Visual 4.2 * 0 Intel PPro 150MHz Solaris 2.5.1 gcc v2.7.2, -O -ffloat-store * 0 Intel PPro 150MHz Solaris 2.5.1 sun cc PC3.0.1 21 Jan 1995, -O -fsimple * 0 Intel Pentium Linux 2.0.23 Gnu gcc 2.6.3 * 0 Intel Pentium OS/2 Warp Gnu gcc 2.7.2.1 + emx 0.9c + fix02 * 0 Intel Pentium OS/2 Warp IBM Visual Age C++ 3.0 * 0 Intel Pentium 4 OS/2 eCS 1.1 Gnu gcc 2.8.1 + emx 0.9d w/ TEST_RND_UP_DECADE * 0 Intel Pentium 4 OS/2 eCS 1.1 Open Watcom C/C++ v1.5 w/ TEST_RND_UP_DECADE * 0 Intel Pentium 4 SuSE Linux 9.1 gcc 3.3.3-41 + glibc 2.3.3-97 * 0 Intel Pentium 4 Windows XP Metrowerks CodeWarrior Pro 8.3 * 0 Intel Pentium 4 Windows XP Metrowerks CodeWarrior Pro 9.2 * 0 Intel Pentium 4 Windows XP Microsoft C/C++ V 12.00 aka Visual 6.0 * 0 Intel Pentium 4 Windows XP Microsoft C/C++ V 14.00 aka Visual Studio 8 * 0 Intel Pentium III RedHat Linux 8 Comeau C/C++ 4.3.0.1 + glibc 2.2.93 * 0 Intel Pentium III RedHat Linux 8 Gnu gcc 3.2 + glibc 2.2.93 * 0 Intel Pentium III RedHat Linux 8 Intel C/C++ v7 + glibc 2.2.93 * 0 Intel Pentium MMX 200 Linux 2.0.30 /usr/bin/cc * 0 Intel Pentium MMX 200 Linux 2.0.30 /usr/bin/g++ * 0 Intel Pentium MMX 200 Linux 2.0.30 /usr/bin/gcc * 0 Intel Pentium/90 Windows NT 4.0 IBM Visual Age C/C++ V3.5 * 0 Intel Pentium/90 Windows NT 4.0 Microsoft C/C++ Visual 4.2 * 0 Intel PentiumPro OS/2 4 Merlin Gnu gcc 2.8.1 + emx 0.9d * 0 Intel PentiumPro OS/2 4 Merlin Metrowerks CodeWarrior Pro 4 * 0 Intel PentiumPro OS/2 4 Merlin Metrowerks CodeWarrior Pro 5 * 0 Intel PentiumPro Windows NT V4.0 Microsoft C++ Visual 4.2 * 0 Intel PentiumPro/200 Windows NT 4.0 Intel C/C++ V2.4 -Op -Za -Od * 0 Intel PentiumPro/350 Windows NT 4.0 SP3 Microsoft C/C++ V 12.00 aka Visual 6.0 * 0 Intel x86 Solaris 2.6 cc (Workshop 4.2) * 0 Intel x86 Solaris 7 cc (Workshop 5.0) * 0 Intel x86 Windows NT 4.0 SP4 Visual C++ 5.0 * 0 Intel x86 Windows NT 4.0 SP4 Visual C++ 6.0 SP2 * 0 Meiko CS2 Meiko/SunOS 5.3 Gnu gcc 2.6.3 * 0 Meiko CS2 Meiko/SunOS 5.3 KAI KCC 3.0g * 0 Meiko CS2 Meiko/SunOS 5.3 PGI pgcc 1.4 * 0 Meiko CS2 Meiko/SunOS 5.3 Sun cc/CC 4.0 * 0 Motorola M-CORE SDS simulator dcc (Diab Data 4.3a) * 0 Motorola M-CORE SDS simulator dcc (Diab Data 4.3b) * 0 StrongARM+FPE 1.07M RiscBSD 1.2 gcc 2.7.2.1 * 0 Sun 4m sparc Sun OS 5.4 Gnu gcc 2.7.0 * 0 Sun 4m sparc Sun OS 5.4 Sun C 4.0 * 0 Sun 4m sparc Sun OS 5.4 cc (C 4.0) * 0 Sun 4m sparc Sun OS 5.5.1 cc (C 4.0) * 0 Sun SPARC 20/512 Solaris 2.6 /opt/SUNWspro/bin/CC * 0 Sun SPARC 20/512 Solaris 2.6 /opt/SUNWspro/bin/c89 * 0 Sun SPARC 20/512 Solaris 2.6 /opt/SUNWspro/bin/cc * 0 Sun SPARC 20/512 Solaris 2.6 /usr/local/bin/g++ * 0 Sun SPARC 20/512 Solaris 2.6 /usr/local/bin/gcc * 0 Sun SPARC 20/512 Solaris 2.6 /usr/local/bin/lcc -A -A * 0 Sun SparcStation4 Solaris 2.5.1 gcc v2.7.2, -O -ffloat-store * 0 Sun SparcStation4 Solaris 2.5.1 sun cc SC4.0 18 Oct 1995 C 4.0, -O -fsimple=0 * 0 Sun UltraSPARC Sun Solaris 2.5 Apogee-C SPARC rel 4.0 -fast * 0 Sun sparc Sun OS 5.5.1 ACE CoSy cc * 0 Unisys A18 MCP (SSR 44.1) A Series C++ * 1 Intel Pentium III RedHat Linux 7 Gnu gcc 2.96 + libc 2.1.92 * 1 Intel Pentium III RedHat Linux 7 Intel icc 7 in C++ mode * 1 Sun SPARC 4/380 SunOS 4.1.3 /usr/lang/acc * 1 Sun SPARC 4/380 SunOS 4.1.3 /usr/local/bin/g++ * 1 Sun SPARC 4/380 SunOS 4.1.3 /usr/local/bin/gcc * 1 Sun SPARC 4/380 SunOS 4.1.3 /usr/local/bin/lcc -A -A * 2 Sun 4m SPARC Sun OS 4.1.4 Gnu GCC 2.6.0 * 2 Sun 4m sparc Sun OS 4.1.4 Gnu gcc 2.7.0 * 25 Intel x86 DG/UX 5.4? cc (Data General 4.20MU04?) * 25 Ultra Sparc UXP/DS V20L10 fcc (Fujitsu C V12L10) * 86 IBM PowerPC 43P AIX 4.1 /bin/c89 * 86 IBM PowerPC 43P AIX 4.1 /bin/cc * 86 IBM PowerPC 43P AIX 4.1 /bin/xlC * 86 IBM PowerPC 43P AIX 4.1 /usr/bin/c89 * 86 IBM PowerPC 43P AIX 4.1 /usr/bin/cc * 86 IBM PowerPC 43P AIX 4.1 /usr/local/bin/g++ * 86 IBM PowerPC 43P AIX 4.1 /usr/local/bin/gcc * 86 IBM RS6000 7006/42T AIX 4.2.0.0 c89 (xlC.C 3.1.3.5) * 86 IBM RS6000 7010/250 AIX 4.1.4.0 c89 (xlC.C 3.1.3.5) * 231 HP 9000 750 HP-UX A.09.05 c89 (A.09.77) * 231 HP 9000 777/C100 HP-UX A.09.07 c89 (A.09.73) * 231 HP 9000/735 HP-UX 9.07 Gnu gcc 2.7.0 * 231 HP 9000/735 HP-UX 9.07 HP cc 9.73 -Ae * 231 Hitachi 3027-G32/3050 HI-UX 04.00 c89 * 233 IBM PowerPC 43P AIX 4.2 /bin/c89 * 233 IBM PowerPC 43P AIX 4.2 /bin/cc * 233 IBM PowerPC 43P AIX 4.2 /bin/xlC * 233 IBM PowerPC 43P AIX 4.2 /usr/bin/c89 * 233 IBM PowerPC 43P AIX 4.2 /usr/bin/cc * 233 IBM PowerPC 43P AIX 4.2 /usr/local/bin/g++ * 233 IBM PowerPC 43P AIX 4.2 /usr/local/bin/gcc * 233 IBM RS6000 7030/3AT AIX 4.1.5.0 c89 (xlC.C 3.1.4.0) * 233 Sony NWS-5000 VP NEWS-OS 6.1.1 cc (ANSI C 3.18) * 281 HP 9000 735 HP-UX B.10.20 c89 (A.10.32.03) * 281 HP 9000/735 HP-UX 10.01 /bin/c89 * 281 HP 9000/735 HP-UX 10.01 /usr/bin/c89 * 281 HP 9000/735 HP-UX 10.01 /usr/local/bin/g++ * 281 HP 9000/735 HP-UX 10.01 /usr/local/bin/gcc * 281 HP 9000/770 HP-UX B.10.01 Gnu gcc 2.7.0 * 281 HP 9000/770 HP-UX B.10.01 HP cc A.10.11 + libc 76.3 -Ae * 281 HP 9000/819 HP-UX B.10.1 HP cc 10.30 -Ae * 281 HP PA-RISC 9000/770 HP-UX B.10.01 HP PA ANSI C * 282 HP 9000/819 HP-UX B.10.1 HP CC (V?) * 617 Motorola M-CORE SDS simulator dcc (Diab Data 4.1a) * 617 Motorola M-CORE SDS simulator dcc (Diab Data 4.2a) * 671 IBM S/390 OS/390 V2R5 IBM C/C++ V2R4 (MIN_WIDTH=32) * 766 IBM S/390 OS/390 V2R5 IBM C/C++ V2R4 (MIN_WIDTH=64) * 2,161 Intel Pentium 4 OS/2 eCS 1.1 Open Watcom C/C++ v1.0 * 2,251 Intel Pentium 4 OS/2 eCS 1.1 Open Watcom C/C++ v1.0 w/ TEST_RND_UP_DECADE * 2,251 Intel Pentium 4 OS/2 eCS 1.1 Open Watcom C/C++ v1.3 w/ TEST_RND_UP_DECADE * 9,658 Intel Pentium Linux 2.0.23 KnoSof OSPC mcc 4.1 + mce * 12,361 Intel 80486DX OS/2 Warp Gnu gcc 2.5.7 + emx 0.8h * 12,361 Intel 80486DX OS/2 Warp Gnu gcc 2.7.2 + emx 0.9b + fix05 * 12,361 Intel Pentium 4 OS/2 eCS 1.1 Gnu gcc 2.5.7 + emx 0.8h w/ TEST_RND_UP_DECADE * 12,361 Intel Pentium 4 OS/2 eCS 1.1 Gnu gcc 2.7.2 + emx 0.9b + fix05 w/ TEST_RND_UP_DECADE * 13,391 Intel Pentium 4 OS/2 eCS 1.1 Gnu gcc 2.5.7 + emx 0.8h w/ TEST_RND_UP_DECADE ??? * 13,391 Intel Pentium 4 OS/2 eCS 1.1 Gnu gcc 2.7.2 + emx 0.9b + fix05 w/ TEST_RND_UP_DECADE * 16,430 Intel Pentium 4 OS/2 eCS 1.1 Open Watcom C/C++ v1.4 w/ TEST_RND_UP_DECADE * 27,147 Cray T3D SC256-8-4 UNICOS MAX 1.3.0.3 CRI mpp cc 4.0.5 * 27,148 Cray T3D SC256-8-4 UNICOS MAX 1.3.0.3 CRI mpp CC 1.0.3.4 * 32,381 MIPS R10000 UX/4800 R13.5 NEC R13.5 * 32,381 MIPS R4400 UX/4800 R11.5 NEC R11.5 * 33,543 Intel 80486DX OS/2 Warp Gnu gcc 2.7.2.1 + emx 0.9c + fix02 * 33,543 Intel Pentium OS/2 Warp Gnu gcc 2.7.2.1 + emx 0.9c * 33,543 Intel Pentium 4 OS/2 eCS 1.1 Gnu gcc 2.7.2.1 + emx 0.9c + fix02 w/ TEST_RND_UP_DECADE * 33,543 Intel Pentium 4 Windows XP Digital Mars C/C++ V8.34 * 33,543 Intel Pentium 4 Windows XP Digital Mars C/C++ V8.38 * 33,543 Intel Pentium 4 Windows XP Digital Mars C/C++ V8.39 * 33,543 Intel Pentium III Windows 98 Digital Mars C/C++ V8.20 * 35,331 Intel Pentium 4 Windows XP Digital Mars C/C++ V8.20 w/ TEST_RND_UP_DECADE * 35,331 Intel Pentium 4 Windows XP Digital Mars C/C++ V8.34 w/ TEST_RND_UP_DECADE * 35,331 Intel Pentium 4 Windows XP Digital Mars C/C++ V8.38 w/ TEST_RND_UP_DECADE * 35,331 Intel Pentium 4 Windows XP Digital Mars C/C++ V8.39 w/ TEST_RND_UP_DECADE * 35,331 Intel Pentium 4 Windows XP Digital Mars C/C++ V8.47 w/ TEST_RND_UP_DECADE * 35,331 Intel Pentium 4 Windows XP Digital Mars C/C++ V8.48 w/ TEST_RND_UP_DECADE * 35,331 Intel Pentium 4 Windows XP Digital Mars C/C++ V8.50 w/ TEST_RND_UP_DECADE * 45,108 MC68030/MC68882 System V.3 TAU ACE Expert C alfa-8.21 * 50,182 Cray J90 UNICOS 9.0 CRI cc 5.0.1.0, * 50,183 Cray J90 UNICOS 9.0 CRI CC 2.0.1 * 62,255 IBM RS6000 6015/DD1 AIX 4.1.2 c89 (xlC.C 3.1.1.0) * 62,255 IBM RS6000 7011/230 AIX 3.2.5 c89 (xlCcmp.* 2.1.3.0) * 62,286 Intel 80486 ISC 4.0.1 gcc 2.7.2 * 62,286 Intel x86 DYNIX/ptx V4.4.4 cc (Sequent V4.4.4) * 62,287 Mips R4400+R4000 SGI Irix64 6.2 cc (C 6.2) * 62,287 SGI O2 R10000-SC IRIX 6.3 /bin/CC * 62,287 SGI O2 R10000-SC IRIX 6.3 /usr/bin/CC * 62,287 SGI O2 R10000-SC IRIX 6.3 /usr/local/bin/g++ * 62,287 SGI O2 R10000-SC IRIX 6.3 /usr/local/bin/gcc * 62,287 SGI Origin/200-4 IRIX 6.4 /bin/CC * 62,287 SGI Origin/200-4 IRIX 6.4 /bin/c89 * 62,287 SGI Origin/200-4 IRIX 6.4 /bin/cc * 62,287 SGI Origin/200-4 IRIX 6.4 /usr/bin/CC * 62,287 SGI Origin/200-4 IRIX 6.4 /usr/bin/c89 * 62,287 SGI Origin/200-4 IRIX 6.4 /usr/bin/cc * 62,287 SGI Origin/200-4 IRIX 6.4 /usr/local/bin/g++ * 62,287 SGI Origin/200-4 IRIX 6.4 /usr/local/bin/gcc * 62,288 SGI O2 R10000-SC IRIX 6.3 /bin/c89 * 62,288 SGI O2 R10000-SC IRIX 6.3 /bin/cc * 62,288 SGI O2 R10000-SC IRIX 6.3 /usr/bin/c89 * 62,288 SGI O2 R10000-SC IRIX 6.3 /usr/bin/cc * 62,291 DEC Alpha OSF/1 3.2 Gnu gcc 2.7.2.1 * 62,291 DEC Alpha OSF/1 3.2 cc + sprintf 4.2.11.2 * 62,291 DEC Alpha 2100-5/250 OSF/1 3.2 /bin/cxx -x cxx * 62,291 DEC Alpha 2100-5/250 OSF/1 3.2 /usr/local/bin/g++ * 62,291 DEC Alpha 2100-5/250 OSF/1 3.2 /usr/local/bin/gcc * 62,291 DEC Alpha 2100-5/250 OSF/1 3.2 /usr/local/bin/lcc -A -A * 62,291 DEC Alpha 250 4/266 OSF/1 V4.0 cc (DEC C V5.2-023) * 62,291 DEC Alpha 4000/610 OSF/1 V3.2C cc * 62,291 DECstation 5000/200 ULTRIX 4.3 /usr/local/bin/lcc -A -A * 62,291 Intel x86 NCR 3.01.01 NCR cc 3.02 * 62,291 Mips IP22 SGI Irix 5.2 Gnu gcc 2.7.2.f.1 * 62,291 Mips IP22 SGI Irix 5.2 cc * 62,291 Mips R4400+R4010 SGI Irix 5.3 cc (C 3.19) * 62,291 SGI Challenge L IRIX 5.3 /bin/CC * 62,291 SGI Challenge L IRIX 5.3 /usr/bin/CC * 62,291 SGI Challenge L IRIX 5.3 /usr/local/bin/g++ * 62,291 SGI Challenge L IRIX 5.3 /usr/local/bin/gcc * 62,291 SGI Challenge L IRIX 5.3 /usr/local/bin/lcc -A -A * 62,292 DEC Alpha 2100-5/250 OSF/1 3.2 /bin/c89 * 62,292 DEC Alpha 2100-5/250 OSF/1 3.2 /bin/cc * 62,292 DEC Alpha 2100-5/250 OSF/1 3.2 /usr/bin/c89 * 62,292 DEC Alpha 2100-5/250 OSF/1 3.2 /usr/bin/cc * 62,292 DEC Alpha 2100-5/250 OSF/1 3.2 /usr/ccs/bin/c89 * 62,292 DEC Alpha 2100-5/250 OSF/1 3.2 /usr/ccs/bin/cc * 62,292 DEC Alpha 2100-5/250 OSF/1 3.2 /usr/ucb/cc * 62,292 DEC Alpha 4100 5/600 Digital Unix V4.0D cc (DEC C V5.6-079) * 62,292 DECstation 5000/200 ULTRIX 4.3 /usr/local/bin/g++ * 62,292 DECstation 5000/200 ULTRIX 4.3 /usr/local/bin/gcc * 62,292 SGI Challenge L IRIX 5.3 /bin/cc * 62,292 SGI Challenge L IRIX 5.3 /usr/bin/cc * 62,581 Cray YMP UNICOS 9.0 CRI cc 4.0.4.1 -D_CRAYIEEE -D_LD64 * 63,126 Cray YMP UNICOS 9.0 CRI CC 2.0.0.3 -D_CRAYIEEE -D_LD64 * 66,287 Intel Pentium 4 Windows XP Digital Mars C/C++ V8.19 w/ TEST_RND_UP_DECADE * 66,718 Intel x86 NCR 3.02 NCR cc 3.03 * 66,718 Intel x86? SCO UnixWare 7.1 cc 3.2 * 67,485 Intel 80486/66 Windows 95 Borland C/C++ V5.01 * 67,485 Intel Pentium/90 Windows NT 4.0 Borland C/C++ V5.01 * 67,488 Intel 80486DX PC DOS Borland C/C++ 4.00 in C mode * 67,488 Intel 80486DX PC DOS Borland Turbo C 2.01 * 67,488 Intel 80486DX Windows 3.1 Borland C/C++ 4.00 in C++ mode * 67,488 Intel Pentium PC DOS Borland Turbo C 2.01 * 67,488 Intel Pentium 4 PC DOS Borland C/C++ 4.00 in C mode w/ TEST_RND_UP_DECADE * 67,488 Intel Pentium 4 PC DOS Borland C/C++ 4.00 in C++ mode w/ TEST_RND_UP_DECADE * 67,488 Intel Pentium 4 PC DOS Borland Turbo C 2.01 w/ TEST_RND_UP_DECADE * 67,488 Intel Pentium Pro Windows NT 4.0 SP3 Borland C/C++ V5.01 (16-bit C mode) * 67,788 Intel Pentium III Windows 98 Digital Mars C/C++ V8.19 * 70,005 Intel 80486/66 Windows 95 Symantec C/C++ V7.01 * 70,005 Intel 80486DX PC DOS Symantec C/C++ 7.0r1x in C mode * 70,005 Intel 80486DX PC DOS Symantec C/C++ 7.0r1x in C++ mode * 70,005 Intel Pentium 4 PC DOS Symantec C/C++ 7.0r1x in C mode w/ TEST_RND_UP_DECADE * 70,005 Intel Pentium III Windows 98 Digital Mars C/C++ V8.16 * 70,005 Intel Pentium/90 Windows NT 4.0 Symantec C/C++ V7.01 * 71,279 NeXT Turbostation Mach 3.3 /bin/cc * 71,279 NeXT Turbostation Mach 3.3 /usr/local/bin/g++ * 71,279 NeXT Turbostation Mach 3.3 /usr/local/bin/gcc * 72,821 Intel Pentium 4 Windows XP LCC 2002/02/10 w/ TEST_RND_UP_DECADE * 72,821 Intel Pentium 4 Windows XP LCC 2004/07/27 w/ TEST_RND_UP_DECADE * 72,828 Intel Pentium III Windows 98 LCC 2002/02/10 * 72,828 Intel Pentium III Windows 98 LCC 2003/01/12 * 73,214 DEC Alpha OpenVMS 7.1 DEC C V5.7 (G float format) * 73,214 DEC Alpha OpenVMS 7.1 DEC C V5.7 (IEEE float format) * 75,179 Intel Pentium 4 Windows XP LCC 2006/04/10 w/ TEST_RND_UP_DECADE * 77,817 NeXT Turbostation Mach 3.3 /usr/local/bin/lcc -A -A * 77,817 Sun 4m sparc Sun OS 4.1.4 Sun acc 3.0.1 *112,761 Cray YMP UNICOS 9.0 CRI cc 4.0.4.1 *113,274 Cray YMP UNICOS 9.0 CRI CC 2.0.0.3 *??????? DECstation 5000/200 ULTRIX 4.3 /bin/cc [would not compile] *??????? DECstation 5000/200 ULTRIX 4.3 /usr/bin/cc [would not compile] *??????? HP 9000/735 HP-UX 10.01 /bin/CC [would not compile] *??????? HP 9000/735 HP-UX 10.01 /bin/cc [would not compile] *??????? HP 9000/735 HP-UX 10.01 /usr/bin/CC [would not compile] *??????? HP 9000/735 HP-UX 10.01 /usr/bin/cc [would not compile] *??????? HP 9000/735 HP-UX 10.01 /usr/ccs/bin/cc [would not compile] *??????? Intel Pentium III RedHat Linux 7 Intel icc 7 [would not compile] *??????? SGI O2 R10000-SC IRIX 6.3 /usr/local/bin/lcc -A -A [would not link] *??????? SGI Origin/200-4 IRIX 6.4 /usr/local/bin/lcc -A -A [garbage output] *??????? Sun SPARC 20/512 Solaris 2.6 /usr/ucb/cc [would not compile] *??????? Sun SPARC 4/380 SunOS 4.1.3 /bin/cc [missing float.h] *??????? Sun SPARC 4/380 SunOS 4.1.3 /usr/bin/cc [missing float.h] *??????? Sun SPARC 4/380 SunOS 4.1.3 /usr/lang/CC [would not compile] *??????? Sun SPARC 4/380 SunOS 4.1.3 /usr/ucb/cc [missing float.h] *coredmp IBM S/390 OS/390 V2R5 IBM C/C++ V2R4 (runtime core dump) *coredmp Intel 80486/66 Windows 95 Watcom v10.5 (runtime core dump) *coredmp Intel Pentium/90 Windows NT 4.0 Watcom v10.5 (runtime core dump) * * I wish to acknowledge the following people for taking the time and effort * to run this test and sending me their results: * Nelson H. F. Beebe, Center for Scientific Computing, University of Utah * L. Busby * Rex Jaeschke, consultant * Derek Jones * Larry Jones * H. G. McIlvried, III * David S. Schwab * Linda Stanberry * Jonathan Ziebell * * MODIFICATION HISTORY: * * 1996/11/10 - Add tests for more kinds of "rounding" styles. * 1996/11/11 - Add bypass for underflow trap being enabled. * 1996/11/15 - Add bypass for -DBL_MIN_10_EXP being "same" as --308. * 1996/11/15 - Add test/bypass for missing EXIT_SUCCESS, EXIT_FAILURE. * 1996/11/15 - Add test/bypass for sprintf() returning a pointer. * 1996/11/22 - Add test for -DBL_MIN_10_EXP being "same" as --308. * 1996/12/06 - Fix bug of large exponent ranges => print more than MIN_WIDTH. * 1996/12/06 - Add test/bypass for "aggregate initializers not implemented". * 1997/01/10 - Add use of %Le to help find failures via USE_Le. * 1997/01/15 - Reduce nesting level of statements to be less than 15. * 1997/01/22 - Replace many 0s with 0...0 in %e error message. * 1997/01/23 - Allow ability to not print x.50...0e-nn failures. * 1997/01/26 - LCL_* -> LMS_* (reserved by POSIX.2). * 1997/03/18 - Allow for DBL_EPSILON to be non-constant for static init. * 1998/07/07 - Add many more system's results. * 1999/04/16 - Add in full formula for E_WIDTH (as a comment). * 1999/08/11 - Add \n to make PP error message more readable; add more results. * 2000/03/01 - Make classification explicit in each file. * 2000/03/01 - Remove confidential so can post to public ftp site. * 2000/07/23 - Add "using namespace std;" for C++ usage. * 2002/01/18 - Add ability to tailor via external file (TAILOR). * 2003/02/22 - Add DISALLOW_IMPLICIT_FLUSH. * 2003/02/22 - Redo order of messages at end of run. * 2004/07/30 - Update DEBUG section. * 2004/09/01 - Fix code for rnd_ceil and 9.4 becoming 10. * 2004/09/01 - Add TEST_RND_UP_DECADE. * 2004/09/02 - Add test of %#.0f being same as %#.*f (with * being 0). * 2006/07/23 - Mods due to Lint 8. * 2006/07/23 - Update DEBUG section. * 2006/07/23 - Indent #if's. * 2007/11/21 - Mods due to Lint 8. * ***********************************************************************/ /* * Start of user tailorable section. You can either edit this file, * or, do a series of #define's in the file tbin2dec.h to use those values. */ #ifdef TAILOR /* A way to tailor via external file */ #include "tbin2dec.h" /* A place to define CUT_OFF, E_WIDTH, ... */ #endif #ifdef DEBUG /* local override of DISALLOWs to force errors to show */ #ifdef DEBUG2 /* Kills some compilers */ #undef DISALLOW_NAMESPACE_STD #endif #ifdef DEBUG3 #endif #undef DISALLOW_IMPLICIT_FLUSH #define TEST_RND_UP_DECADE #endif #ifndef CUT_OFF #define CUT_OFF 5uL /* Maximum number of %f error messages to print */ #endif #ifndef E_WIDTH #define E_WIDTH 99 /* At least DBL_DIG * 2.5 */ #endif #ifndef MIN_WIDTH #define MIN_WIDTH 509 /* Minimum field width for C90 is 509; for C99 is 4095 */ #endif /* * Pick one of the next two. #undef for Std C, #define for debug. */ #ifdef TRY_LE #define USE_Le /* Use %Le in place of %e for more digits */ #else #undef USE_Le /* Assume %e is accurate and correct */ #endif /* * Pick one of the next two. */ #ifdef NO_MIDS #define SUPPRESS_MIDPOINT /* Do NOT print x.50...0e-nn failures */ #else #undef SUPPRESS_MIDPOINT /* Do print x.50...0e-nn failures */ #endif /* * If you wish to test rounding up that spans a decade boundary, * then define this symbol. This is a harder test than the default * which does not span a decade boundary. This is not the default * because I have too many results before 2004/09/01 that did not * have this option. */ /* #define TEST_RND_UP_DECADE */ /* * End of user tailorable section. Rest of code should not need modification. */ #include /* sprintf(), printf() */ #include /* errno */ #include /* DBL_DIG, DBL_EPSILON, FLT_RADIX, ... */ #include /* exit(), EXIT_SUCCESS, EXIT_FAILURE */ #ifndef DISALLOW_NAMESPACE_STD #ifdef __cplusplus /* For C++ to call C functions */ using namespace std; /* Add implicit std:: to each function call */ #endif #endif enum how_round { add_5_chop, sub_5_ceil, near_even, near_odd, rnd_floor, rnd_ceil, rnd_weird }; static unsigned long failures = 0uL; /* Number of failures */ static unsigned long printed = 0uL; /* Number of %f failures printed */ static unsigned long midpoint = 0uL; /* Number of x.50...0e-nn failures */ static int test_rc = 1; /* Should we test return code of sprintf() */ /*lint -e774 // boolean within 'if' always evaluates to false */ /*lint -e777 // testing float's for equality */ /*lint -e788 // enum constant not used with defaulted switch */ /*lint -e801 // use of goto is deprecated */ /*lint -e909 // implicit conversion from int to bool */ /*lint -e910 // implicit conversion from 0 to ptr */ /*lint -e944 // argument for operator always evalutes to false */ /*lint -e946 // subtract applied to pointers */ /*lint -e947 // subtract applied to pointers */ /*lint -e953 // var could be declared as const */ /*lint -e973 // unary operator in macro not parenthesized */ /*==============================================================*/ /* * Check for non-conformance to Standard C for issues other than * the main purpose of this test. These other faults were found * by various users of this test on their systems and had to be * bypassed before the real test could be run. */ static void stdc_failures(void) { /* * If any of the array initializers fail at compile time, * change the #if 1 to #if 0 */ #if 1 auto char str0[] = {"automatic array initializer\n"}; static char str1[] = {"static array initializer\n"}; auto const char str2[] = {"automatic const array initializer\n"}; static const char str3[] = {"static const array initializer\n"}; auto double v0[] = {0.0, 1.0}; static double v1[] = {0.0, 1.0}; auto const double v2[] = {0.0, 1.0}; static const double v3[] = {0.0, 1.0}; #else #define LMS_INITIALIZERS #endif #ifdef LMS_INITIALIZERS (void) printf("FAIL: Aggregate initializers not supported\n"); failures++; #else if ((str0[0] == str2[0]) && (str1[0] == str3[0])) { if ((v0[0] == v2[0]) && (v1[0] == v3[0])) { ; /* use the vars */ } } #endif #ifndef EXIT_SUCCESS /* Missing from vendor's stdlib.h */ #define EXIT_SUCCESS 0 #define LMS_MISSING_EXIT #endif #ifndef EXIT_FAILURE /* Missing from vendor's stdlib.h */ #define EXIT_FAILURE 1 #define LMS_MISSING_EXIT #endif #ifdef LMS_MISSING_EXIT (void) printf("FAIL: EXIT_FAILURE or EXIT_SUCCESS is missing from stdlib.h\n"); failures++; #endif /* * This code attempts to detect if sprintf() returns an int (as per * Standard C), or a pointer (which is what some compilers do). * * If sprintf() returns an int as per Standard C, then the tests of the * integer value returned by sprintf() will be done (test_rc = 1). * * If sprintf() returns a pointer (as some compilers have it), then the * tests of the return code will be ignored (test_rc = 0). */ { /* local block */ int rc; /* return code from sprintf() */ char out[99]; /* only need room for 4 */ rc = sprintf(out, "%#.*f", 1, 0.5); /* FUT */ if (rc != 3) { /* 3 is an unusual value for an address */ (void) printf("FAIL: sprintf() appears to return(%i) a pointer (should be an int)\n", rc); failures++; test_rc = 0; /* Turn off testing of return code */ } } /* * This code attempts to detect if the preprocessor treats pp-tokens * and tokens as per Standard C. * * In the following, -Z expands into --pp, which looks like a prefix * decrement operator (--) applied to pp. But, -- really is two * separate pp-tokens and is effectivly, - -pp, which is just the value * of pp. If this bug exists, then the bypass is -(Z) or - Z * This bug effected usage of DBL_MIN_10_EXP later in code. */ { /* local block */ #define Z -pp int pp, pp4, pp5; pp = 5; pp5 = -Z; /* expands into --pp, but treated like - -pp */ pp4 = --pp; if ((pp5 != 5) || (pp4 != 4) || (pp != 4)) { (void) printf("FAIL: Preprocessor expansion is not Standard C conforming.\n"); (void) printf("\tpp =%i (should be 4).\n", pp); (void) printf("\tpp4=%i (should be 4).\n", pp4); (void) printf("\tpp5=%i (should be 5).\n", pp5); failures++; } #undef Z } } /*================================================================*/ /* * Find value of Unit in Last Place of argument. * lower_power <= x < upper_power, eg, radix**j <= x < radix**(j+1) * ulp in upward direction = epsilon * lower_power * ulp in downward direction = epsilon * lower_power / radix if x is * exactly a power of radix, else, same as ulp in upward direction. */ static double ulp(double x, const int dir) { static double lower_power = 1.0; static double upper_power = (double) FLT_RADIX; static double last_ulp = -1.0 /* really want DBL_EPSILON */; double max_power = DBL_MAX / (double) FLT_RADIX; double one_less = 1.0 - DBL_EPSILON / (double) FLT_RADIX; const double one_over = 1.0 / (double) FLT_RADIX; const double radix = (double) FLT_RADIX; if (last_ulp < 0.0){ /* In case DBL_EPSILON is not a constant */ last_ulp = DBL_EPSILON; } if (x != x) { return x; /* Not-a-Number */ } if (x < 0.0) { x = -x; } if ((x < lower_power) || (upper_power <= x)) { if (x == 0.0) { return 0.0; /* 0.0 */ } if (DBL_MAX < x) { return x; /* Infinity */ } if (x < DBL_MIN) { /* denormal numbers */ upper_power = DBL_MIN; last_ulp = upper_power * DBL_EPSILON; lower_power = 0.0; /* in case dir < 0 */ } else if (max_power < x) { /* in top 'binade' */ lower_power = max_power / one_less; upper_power = lower_power; /* lie to prevent overflow */ last_ulp = lower_power * DBL_EPSILON; } else { if (x < lower_power) { do { lower_power *= one_over; } while (x < lower_power); upper_power = lower_power * radix; last_ulp = lower_power * DBL_EPSILON; } else if (upper_power <= x) { do { upper_power *= radix; } while (upper_power <= x); lower_power = upper_power * one_over; last_ulp = lower_power * DBL_EPSILON; } else { (void) printf("Should not happen in ulp()\n"); } } } if ((dir < 0) && (x == lower_power)) { /* power of radix */ return last_ulp * one_over; } else { return last_ulp; } } /*================================================================*/ /* * Determine how this system rounds. Known rounding methods are: * 0) Add 0.5 and truncate fraction. X.5 is always rounded up. * 1) Add 0.4999... and truncate fraction; or subtract 0.5 and ceil. * It is the same as 1) except X.5 is always rounded down. * 2) Round to nearest even. It is the same as 1) except if the number * is exactly on the 0.5 boundry. Then the number is rounded so that * the result is an even number. That way 0.5, 2.5, 4.5, 6.5, and 8.5 * round down and 1.5, 3.5, 5.5, 7.5, and 9.5 round up. * 3) Round to nearest odd. It is the same as 1) except if the number * is exactly on the 0.5 boundry. Then the number is rounded so that * the result is an odd number. That way 0.5, 2.5, 4.5, 6.5, and 8.5 * round up and 1.5, 3.5, 5.5, 7.5, and 9.5 round down. * 4) floor, or round down towards -infinity. * 5) ceil, or round up towards +infinity. * * If sprintf() returns a pointer (instead of the expected int), then * sprintf() will return a random integer and the tests of the count * will be bypassed. */ #define ONE_DIGIT( buffer, val ) \ errno = 0;\ rc = sprintf( buffer, "%#.0f", val );\ e = errno;\ if (0 != e) {\ (void) printf("errno is %d, expected 0 in line %d\n", e, (int) __LINE__);\ }\ if (test_rc && (2 != rc)) {\ (void) printf("count is %d, expected 2 in line %d\n", rc, (int) __LINE__);\ (void) printf(" buf is %s\n", buffer);\ } static enum how_round find_round(void) { double v15 = 3. / 2.; double v25 = 5. / 2.; double v35 = 3.25; double v45 = 4.75; int e; int rc; enum how_round rounds = rnd_weird; /* will be set before return */ char buf1[9]; char buf2[9]; char buf3[9]; char buf4[9]; char bufa[9]; char bufb[9]; char bufc[9]; char bufd[9]; if( 3. != (v15+v15) ){ (void) printf("FAIL: 3./2. not exactly 1.5, results suspect\n"); failures++; } if( 5. != (v25+v25) ){ (void) printf("FAIL: 5./2. not exactly 2.5, results suspect\n"); failures++; } ONE_DIGIT(buf1, v15); /* 1.50 --> 1. or 2. */ ONE_DIGIT(buf2, v25); /* 2.50 --> 2. or 3. */ ONE_DIGIT(buf3, v35); /* 3.25 --> 3. or 4. */ ONE_DIGIT(buf4, v45); /* 4.75 --> 4. or 5. */ #ifdef DEBUG (void) printf("Values used to determine rounding are:\n"); (void) printf(" 1.5 --> %s, 2.5 --> %s, 3.25 --> %s, 4.75 --> %s\n", buf1, buf2, buf3, buf4); #endif /* * Now that we have the raw data, determine how round. */ if (('3' == buf3[0]) && ('5' == buf4[0])) { /* rounds */ if (('2' == buf1[0]) && ('3' == buf2[0])) { rounds = add_5_chop; } else if (('2' == buf1[0]) && ('2' == buf2[0])) { rounds = near_even; } else if (('1' == buf1[0]) && ('3' == buf2[0])) { rounds = near_odd; } else if (('1' == buf1[0]) && ('2' == buf2[0])) { rounds = sub_5_ceil; } else { (void) printf("FAIL: Cannot determine how system rounds.\n"); (void) printf("1.5 --> %s, 2.5 --> %s, 3.25 --> %s, 4.75 --> %s\n", buf1, buf2, buf3, buf4); } } else { /* floor or ceil */ if (('1' == buf1[0]) && ('2' == buf2[0]) && ('3' == buf3[0]) && ('4' == buf4[0])) { /* floor */ rounds = rnd_floor; } else if (('2' == buf1[0]) && ('3' == buf2[0]) && ('4' == buf3[0]) && ('5' == buf4[0])) { /* ceil */ rounds = rnd_ceil; } else { (void) printf("FAIL: Cannot determine how system rounds.\n"); (void) printf("1.5 --> %s, 2.5 --> %s, 3.25 --> %s, 4.75 --> %s\n", buf1, buf2, buf3, buf4); } } (void) sprintf( bufa, "%#.*f", 0, v15); (void) sprintf( bufb, "%#.*f", 0, v25); (void) sprintf( bufc, "%#.*f", 0, v35); (void) sprintf( bufd, "%#.*f", 0, v45); if( (buf1[0] != bufa[0]) || (buf2[0] != bufb[0]) || (buf3[0] != bufc[0]) || (buf4[0] != bufd[0]) ){ failures++; (void) printf("FAIL: %%#.0f not equal to %%#.*f\n"); (void) printf(" buf1=%s, bufa=%s\n", buf1, bufa); (void) printf(" buf2=%s, bufb=%s\n", buf2, bufb); (void) printf(" buf3=%s, bufc=%s\n", buf3, bufc); (void) printf(" buf4=%s, bufd=%s\n", buf4, bufd); } return rounds; } /*================================================================*/ /* * Compute the expected digit for %f by using %e. Assumes %e is good. * %e will produce one of these forms: d.edd or d.ddddedd */ static char compute_digit(double val, char ebuf[], const enum how_round rounds, int *half_way) { char dig_want = (char) '?'; if (ebuf && half_way) { #ifdef USE_Le (void) sprintf(ebuf, "%#.*Le", E_WIDTH, (long double) val); #else (void) sprintf(ebuf, "%#.*e", E_WIDTH, val); /* Prefered */ #endif *half_way = 0; /* assume not X.5000...0e-NN */ if (('e' == ebuf[2]) /* X.e-NN */ ||(rounds == rnd_floor)) { /* floor */ dig_want = ebuf[0]; } else if ((rounds == rnd_ceil) /* ceil; may be bigger than '9' */ ||('5' < ebuf[2])) { /* big digits */ dig_want = (char) (ebuf[0] + 1); /* round up */ } else if (ebuf[2] < '5') { /* little digits */ dig_want = ebuf[0]; /* round down */ } else { /* '5' == ebuf[2] */ if (rounds == add_5_chop) { dig_want = (char) (ebuf[0] + 1); /* round up */ } else { /* X.5000...digit or X.5000...0e-NN */ int fjt = 3; /* index of first non-zero after 5 */ while ('0' == ebuf[fjt]) { fjt++; } if ('e' == ebuf[fjt]) { *half_way = 1; /* X.5000...0e-NN */ switch (rounds) { case sub_5_ceil: dig_want = (char) (ebuf[0]); /* round down */ break; case near_even: switch (ebuf[0]) { /* round to even */ case '1': case '3': case '5': case '7': case '9': dig_want = (char) (ebuf[0] + 1); break; case '0': case '2': case '4': case '6': case '8': dig_want = ebuf[0]; break; default: (void) printf("Should not happen in line %d\n", (int) __LINE__); } /* switch */ break; case near_odd: switch (ebuf[0]) { /* round to odd */ case '1': case '3': case '5': case '7': case '9': dig_want = ebuf[0]; break; case '0': case '2': case '4': case '6': case '8': dig_want = (char) (ebuf[0] + 1); break; default: (void) printf("Should not happen in line %d\n", (int) __LINE__); } /* switch */ break; default: (void) printf("Should not happen in line %d\n", (int) __LINE__); } /* switch */ } else { /* X.5000... non-zero-digit digits e-xxx => round up */ dig_want = (char) (ebuf[0] + 1); } } } } return dig_want; } /*================================================================*/ /* * Replace many 0s with "..." to end up with: x.xxx0...0e-xx */ static void efmt_zeros(char ebuf[]) { char *p; /* walks thru array */ char *q; /* points to 1st 0 of a string of 0s */ if (ebuf) { q = 0; for (p = ebuf; (*p) && ('e' != *p); p++) { if ('0' == *p) { if (0 == q) { q = p; /* 1st 0 after non-0 */ } } else { q = 0; /* indicate non-0 */ } } if (0 != q) { /* string of 0s before the 'e' */ if (5 < (p - q)) { /* have enough 0s to remove */ *(q + 1) = (char) '.'; *(q + 2) = (char) '.'; *(q + 3) = (char) '.'; q += 5; for (;;) { /* copy e-xx part, including nul */ *q = *p; if (!(*p)) { break; /* just copied the trailing nul */ } q++; p++; } } } } } /*================================================================*/ /* * Replace many 0s with "..." to end up with: 0.0...0x (... is nn 0s) */ static void ffmt_zeros(char out[]) { const char *p; /* walks thru array */ char *q; /* points to 1st 0 of a string of 0s */ int removed; /* # of 0s removed */ if (out && (out[2] == '0')) { q = out + 2; /* 1st zero after '.' */ for (p = q; ('0' == *p); p++) { ; /* find 1st non-0 (may be nul) */ } removed = (p - q) - 2; if (5 < (p - q)) { /* have enough 0s to remove */ *(q + 1) = (char) '.'; *(q + 2) = (char) '.'; *(q + 3) = (char) '.'; q += 5; for (;;) { /* copy last part, including nul */ *q = *p; if (!(*p)) { break; /* just copied the trailing nul */ } q++; p++; } (void) sprintf(q, " (... is %i 0s)", removed); } } } /*================================================================*/ /* * Test sprintf to see if it is consistent on rounding and meets ISO C. */ int main(void) { double ten2dig; /* positive powers of 10 */ double scale_up; /* to prevent underflow doing ulps */ double scale_down; /* to prevent underflow doing ulps */ double val; /* value[] / ten2dig */ double value[18]; /* basic value to print */ int max_exp; /* min of MAX_10_EXP, -MIN_10_EXP */ int dig; /* number of digits testing */ int pt; /* have a decimal point? */ int e; int rc; /* return code got from function */ int rc_want; /* return code want from function */ int e_wid; int j; /* loop index */ int kk; /* ulps above or below loop index */ int half_way; /* is it X.5000...0eNN */ int where; /* index into out where check */ enum how_round rounds; char dig_want; /* expected digit from %f */ char out[MIN_WIDTH + 30]; /* room for (... is nnn 0s) */ char ebuf[E_WIDTH + 12]; /* at least DBL_DIG*2.5 + 12 */ /* * At least one compiler does not allow initialization of arrays, so * do it the long way at runtime. */ #ifdef TEST_RND_UP_DECADE (void)printf("Testing with TEST_RND_UP_DECADE\n"); value[0] = 9.5; /* will round up to next decade */ #else value[0] = 9.4; /* should stay in same decade */ #endif value[1] = 9.0; value[2] = 8.5; value[3] = 8.0; value[4] = 7.5; value[5] = 7.0; value[6] = 6.5; value[7] = 6.0; value[8] = 5.5; value[9] = 5.0; value[10] = 4.5; value[11] = 4.0; value[12] = 3.5; value[13] = 3.0; value[14] = 2.5; value[15] = 2.0; value[16] = 1.5; value[17] = 1.1; /* * This is the formula for the minimum number of digits to use * to represent an internal double number in decimal. But, it * is not used since it involves the math library (which makes * running this harder; some people do not know how to link in * a math library). Also, the accuracy of the math library can * be very bad. Could also use DECIMAL_DIG of C99. * * e_wid = (int)ceil(1.0+log10((double)FLT_RADIX)*(double)DBL_MANT_DIG); */ e_wid = (5 * (int) DBL_DIG) / 2; /* Using enough precision? */ if (E_WIDTH < e_wid) { (void) printf("Increase E_WIDTH to at least %i\n", e_wid); } stdc_failures(); /* See if any misc. Standard C problems */ /* * Compute small values to test from X.Y / large value. Therefore, * to prevent both overflow of the large value and underflow of the * small value, we need to limit the powers of 10 that we will use. */ max_exp = -(DBL_MIN_10_EXP); /* ()'s are in case macro is -xxx */ if ((DBL_MAX_10_EXP) < max_exp) { /* min of two ranges */ max_exp = DBL_MAX_10_EXP; } /* * For machines with very large exponent ranges for double, we need to * limit the decades that we check so that we do not try to print more * than MIN_WIDTH characters (all that is required by Standard C). */ if ((MIN_WIDTH - 2) < max_exp) { max_exp = MIN_WIDTH - 2; /* leave room for leading "0." */ } rounds = find_round(); /* find rounding style */ if( rnd_weird == rounds ){ failures++; goto give_up; } scale_down = DBL_EPSILON / (double) FLT_RADIX; /* should be exact */ scale_up = 1.0 / scale_down; /* power of base so that ulp of small will not underflow */ ten2dig = 1.0; /* 10 ** 0 */ pt = 0; /* no decimal point so far */ for (dig = 0; dig <= max_exp; dig++) { /* 0...MIN(DBL_*_10_EXP) */ for (j = 0; j < (int) (sizeof (value) / sizeof (value[0])); j++) { val = value[j] / ten2dig; /* x / 10**(dig) */ val *= scale_up; for (kk = 1; kk <= 7; kk++) { val = val + ulp(val, 1); /* val = nextafter(val,MAX) */ } val *= scale_down; for (kk = 7; -7 < kk; kk--) { /* 7 ulps above to 7 ulps below */ if (val == 0.0) { /* underflowed */ (void) printf("FAIL: Underflow: One of DBL_*_10_EXP is wrong\n"); failures++; goto give_up; } /* * Find expected digit. It will be greater than '9' * if rounding is rnd_ceil and value is greater than 9. * dig val => ceil others * 0 9.4 => 10. 9. * 1 0.94 => 1.0 0.9 * 2 0.094 => 0.10 0.09 * 3 0.0094 => 0.010 0.009 */ dig_want = compute_digit(val, ebuf, rounds, &half_way); where = dig + pt; rc_want = dig + 2; if('9' < dig_want){ /* due to '9'+1 */ dig_want = (char)'1'; /* as if 9+1 became 10 */ if(dig < 2){ where = 0; }else{ where--; /* carry left one digit */ } if(!dig) rc_want++; /* one extra digit for 10. */ } out[where] = (char) '?'; /* init known value */ /* * Now test the Function Under Test (FUT). */ errno = 0; rc = sprintf(out, "%#.*f", dig, val); /* FUT */ e = errno; if ((test_rc && (rc != rc_want)) || e || (out[where] != dig_want)) { failures++; if (half_way) { midpoint++; } if ((printed < CUT_OFF) #ifdef SUPPRESS_MIDPOINT /* Do NOT print x.50...0e-nn failures */ && (!half_way) #endif ) { printed++; (void) printf("FAIL: dig=%i, errno=%i, rc=%i, kk=%i\n", dig, e, rc, kk); #ifdef DEBUG (void) printf(", %%a=>%a\n", val); #endif efmt_zeros(ebuf); /* many 0s to ... */ (void) printf(", %%e=>%s\n", ebuf); ffmt_zeros(out); /* many 0s to ... */ (void) printf(", %%f=>%s\n", out); if (e) { (void) printf(", errno should be 0"); } if (test_rc && (rc != rc_want)) { (void) printf(", rc should be %i", rc_want); } if (half_way) { if (kk < 0) { (void) printf(", %%e should be X.4999...9Y...e-NN"); } else if (0 < kk) { (void) printf(", %%e should be X.5000...0Y...e-NN"); } else { (void) printf(", %%e should not be X.5000...0e-NN"); } } if (out[where] != dig_want) { (void) printf(", %%f[%i] should be %c (based upon %%e)", where, dig_want); } (void) printf("\n"); } } val *= scale_up; val = val - ulp(val, -1); /* val = nextafter(val, 0.0); */ val *= scale_down; } /* kk loop */ } /* j loop */ if (dig == max_exp) { break; /* next would overflow */ } else { double last_pow = ten2dig; /* so can detect overflow */ ten2dig *= 10.0; /* another power of 10 */ if (ten2dig <= last_pow) { /* overflowed */ (void) printf("FAIL: Overflow: One of DBL_*_10_EXP is wrong\n"); failures++; goto give_up; } } pt = 1; /* have a leading decimal point */ } /* dig loop */ /* * Make sure can print out fields up to MIN_WIDTH (at least 509 digits) * %#.507f outputs 509 chars. There is a leading "0." and then the 507 * digits after the decimal point as per the precision. C99 raised the * 509 to 4095. */ for (dig = max_exp; dig <= (MIN_WIDTH - 2); dig++) { out[dig + 1] = (char) '?'; /* so can see if changed to '0' */ errno = 0; rc = sprintf(out, "%#.*f", dig, 0.0); /* FUT */ e = errno; if ((test_rc && (rc != (dig + 2))) || e || (out[dig + 1] != '0')) { failures++; if (printed < CUT_OFF) { printed++; (void) printf("FAIL: dig=%i, errno=%i, rc=%i\n", dig, e, rc); ffmt_zeros(out); /* many 0s to ... */ (void) printf(", %%f=>%s\n", out); if (e) { (void) printf(", errno should be 0"); } if (test_rc && (rc != (dig + 2))) { (void) printf(", rc should be %i", dig + 2); } if (out[dig + 1] != '0') { (void) printf(", out[%i] should be %c", dig + 1, '0'); } (void) printf("\n"); } } } /* dig loop */ give_up: (void) printf("Rounding is "); switch (rounds) { case add_5_chop: (void) printf("add 0.5 and truncate."); break; case sub_5_ceil: (void) printf("add 0.4999... and truncate; or sub 0.5 and ceil."); break; case near_even: (void) printf("round to nearest, even."); break; case near_odd: (void) printf("round to nearest, odd."); break; case rnd_floor: (void) printf("truncate or floor."); break; case rnd_ceil: (void) printf("upwards or ceil."); break; case rnd_weird: default: (void) printf("unknown. Should not happen."); break; } (void) printf("\n"); if (midpoint) { (void) printf("%lu failure(s) were due to %%e producing x.50...0e-nn\n", midpoint); } if (printed >= CUT_OFF) { (void) printf("WARNING: Not all %lu failures were printed.\n", failures); } if (failures) { (void) printf("Failed. %lu failure(s).\n", failures); rc = EXIT_FAILURE; }else{ (void) printf("Passed. MIN_WIDTH=%i.\n", MIN_WIDTH); rc = EXIT_SUCCESS; } #ifdef DISALLOW_IMPLICIT_FLUSH (void)fflush(stdout); /* need explicit flush */ #endif #if (defined RETVAL) && (defined STANDALONE_TEST) rc = RETVAL; /* Being run as part of test suite */ #endif return rc; }