Date: Mon, 17 Jun 2013 08:49:08 +0000 (UTC) From: Peter Jeremy <peterj@FreeBSD.org> To: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: svn commit: r251835 - user/peterj Message-ID: <201306170849.r5H8n89J001608@svn.freebsd.org>
index | next in thread | raw e-mail
Author: peterj Date: Mon Jun 17 08:49:08 2013 New Revision: 251835 URL: http://svnweb.freebsd.org/changeset/base/251835 Log: Add a libm test tool that I developed as part of the FreeBSD numerics work. This program tests libm exception conditions listed in WG14/N1256 G.6 against the set of float, double and long double functions this program is linked against. Note that the libm can be incomplete - this program uses dlfunc(3) to identify which functions are present. It is intended to be portable across operating systems and architectures. Added: user/peterj/ user/peterj/README user/peterj/ctest.c (contents, props changed) Added: user/peterj/README ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/peterj/README Mon Jun 17 08:49:08 2013 (r251835) @@ -0,0 +1,6 @@ +My odds & ends + +ctest.c test libm exception conditions listed in WG14/N1256 G.6 + against a set of float, double and long double functions + +$FreeBSD$ Added: user/peterj/ctest.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/peterj/ctest.c Mon Jun 17 08:49:08 2013 (r251835) @@ -0,0 +1,1241 @@ +/*- + * Copyright (c) 2012 Peter Jeremy <peterj@FreeBSD.ORG> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This program tests libm exception conditions listed in WG14/N1256 G.6 + * against the set of float, double and long double functions this program + * is linked against. Note that the libm can be incomplete - this program + * uses dlfunc(3) to identify which functions are present. + * + * It is intended to be portable across operating systems and architectures. + * + * Command line arguments: + * -v Verify that the various unions are the correct size and that the + * test table is sane rather than performing the tests. + * -r List each function being tested and the arguments being passed. + * + * Default: Only report missing functions and incorrect results. + */ + +#ifdef __FreeBSD +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); +#endif +#include <stdint.h> /* for [u]intNN_t */ +#include <math.h> +#include <complex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#define __USE_GNU /* Linux is broken */ +#include <dlfcn.h> + +#ifndef __FreeBSD +#define dlfunc(a,b) dlsym(a,b) +#endif + +/* long doubles differ between architectures so define appropriate structures for each family */ +#if defined(__i386__) || defined(__amd64__) +/* From FreeBSD: stable/8/lib/libc/amd64/_fpmath.h 175402 2008-01-17 16:39:07Z bde */ +#ifdef __amd64__ +union IEEEl2bits { + long double f; + struct Ieeel2bits { + uint32_t manl :32; + uint32_t manh :32; + uint32_t exp :15; + uint32_t sign :1; + uint32_t junkl :16; + uint32_t junkh :32; + } bits; + struct { + uint64_t man :64; + uint64_t expsign :16; + uint64_t junk :48; + } xbits; + uint64_t i; +}; +#else +union IEEEl2bits { + long double f; + struct Ieeel2bits { + uint32_t manl :32; + uint32_t manh :32; + uint32_t exp :15; + uint32_t sign :1; + uint32_t junkl :16; + } bits; + struct { + uint32_t manl :32; + uint32_t manh :32; + uint32_t expsign :16; + uint32_t junk :16; + } xbits; +}; +#endif + +/* Unlike all the other formats, the units bit is explicit for x87 */ +#define L_ZERO PACK3(0x0000, 0x00000000U, 0x00000000U) +#define L_ONE PACK3(0x3fff, 0x80000000U, 0x00000000U) +#define L_INF PACK3(0x7fff, 0x80000000U, 0x00000000U) +#define L_NAN PACK3(0x7fff, 0xc0000000U, 0x00000000U) +#define L_PI_4 PACK3(0x3ffe, 0xc90fdaa2U, 0x2168c235U) +#define L_PI_2 PACK3(0x3fff, 0xc90fdaa2U, 0x2168c235U) +#define L_3PI_4 PACK3(0x4000, 0x96cbe3f9U, 0x990e91a8U) +#define L_PI PACK3(0x4000, 0xc90fdaa2U, 0x2168c235U) + +/* + * On i386, sizeof(long double) is 12 but at least the Atom N270 only + * actually writes 10 bytes. Since the result validation is done + * using memcmp(), explicitly define the number of bytes to compare. + */ +#define sizeof_long_double 10 +#define INF_EXP 0x7fff +#elif defined(__arm__) +/* From FreeBSD: stable/8/lib/libc/arm/_fpmath.h 186461 2008-12-23 22:20:59Z marcel */ +/* Raspberry Pi is fully little-endian */ +union IEEEl2bits { + long double f; + struct Ieeel2bits { + uint32_t manl :32; + uint32_t manh :20; + uint32_t exp :11; + uint32_t sign :1; + } bits; + struct { + uint64_t man :52; + uint64_t expsign :12; + } xbits; + uint64_t i; +}; + +#define L_ZERO D_ZERO +#define L_ONE D_ONE +#define L_INF D_INF +#define L_NAN D_NAN +#define L_PI_4 D_PI_4 +#define L_PI_2 D_PI_2 +#define L_3PI_4 D_3PI_4 +#define L_PI D_PI + +#define sizeof_long_double sizeof(long double) +#define INF_EXP 0x7ff +#elif defined(__sparc) +/* From FreeBSD: stable/8/lib/libc/sparc64/_fpmath.h 175459 2008-01-18 21:25:51Z das */ +#define FP_BIG_ENDIAN + +union IEEEl2bits { + long double f; + struct Ieeel2bits { + uint32_t sign :1; + uint32_t exp :15; + uint32_t manhh :16; + uint32_t manhl :32; + uint32_t manlh :32; + uint32_t manll :32; + } bits; + struct { + uint64_t expsign :16; + uint64_t manh :48; + uint64_t manl :64; + } xbits; + uint64_t i[2]; +}; + +#define L_ZERO { 0, 0x0000, 0x0000, 0x00000000U, 0x00000000U, 0x00000000U } +#define L_ONE { 0, 0x3fff, 0x0000, 0x00000000U, 0x00000000U, 0x00000000U } +#define L_INF { 0, 0x7fff, 0x0000, 0x00000000U, 0x00000000U, 0x00000000U } +#define L_NAN { 0, 0x7fff, 0x8000, 0x00000000U, 0x00000000U, 0x00000000U } +#define L_PI_4 { 0, 0x3ffe, 0x921f, 0xb54442d1U, 0x8469898cU, 0xc51701b8U } +#define L_PI_2 { 0, 0x3fff, 0x921f, 0xb54442d1U, 0x8469898cU, 0xc51701b8U } +#define L_3PI_4 { 0, 0x4000, 0x2d97, 0xc7f3321dU, 0x234f2729U, 0x93d1414aU } +#define L_PI { 0, 0x4000, 0x921f, 0xb54442d1U, 0x8469898cU, 0xc51701b8U } + +#define sizeof_long_double sizeof(long double) +#define INF_EXP 0x7fff +#endif + +/* float and double are consistent across all architectures apart from endianness */ +union IEEEf2bits { + float f; + uint32_t i; + struct Ieeef2bits { +#ifndef FP_BIG_ENDIAN + uint32_t man :23; + uint32_t exp :8; + uint32_t sign :1; +#else + uint32_t sign :1; + uint32_t exp :8; + uint32_t man :23; +#endif + } bits; +}; + +union IEEEd2bits { + double f; + uint64_t i; + struct Ieeed2bits { +#ifndef FP_BIG_ENDIAN + uint32_t manl :32; + uint32_t manh :20; + uint32_t exp :11; + uint32_t sign :1; +#else /* _BIG_ENDIAN */ + uint32_t sign :1; + uint32_t exp :11; + uint32_t manh :20; + uint32_t manl :32; +#endif + } bits; + struct { +#ifndef FP_BIG_ENDIAN + uint64_t man :52; + uint64_t expsign :12; +#else /* _BIG_ENDIAN */ + uint64_t expsign :12; + uint64_t man :52; +#endif + } xbits; +}; + +typedef struct Ieeef2bits fbits; +typedef struct Ieeed2bits dbits; +typedef struct Ieeel2bits lbits; + +#ifdef FP_BIG_ENDIAN +#define PACK2(exp, man) { 0, exp, man } +#define PACK3(exp, manh, manl) { 0, exp, manh, manl } +#else +#define PACK2(exp, man) { man, exp } +#define PACK3(exp, manh, manl) { manl, manh, exp } +#endif + +#define F_ZERO PACK2(0x00, 0x000000) +#define F_ONE PACK2(0x7f, 0x000000) +#define F_INF PACK2(0xff, 0x000000) +#define F_NAN PACK2(0xff, 0x400000) +#define F_PI_4 PACK2(0x7e, 0x490fdb) +#define F_PI_2 PACK2(0x7f, 0x490fdb) +#define F_3PI_4 PACK2(0x80, 0x16cbe4) +#define F_PI PACK2(0x80, 0x490fdb) + +#define D_ZERO PACK3(0x000, 0x00000, 0x00000000U) +#define D_ONE PACK3(0x3ff, 0x00000, 0x00000000U) +#define D_INF PACK3(0x7ff, 0x00000, 0x00000000U) +#define D_NAN PACK3(0x7ff, 0x80000, 0x00000000U) +#define D_PI_4 PACK3(0x3fe, 0x921fb, 0x54442d18U) +#define D_PI_2 PACK3(0x3ff, 0x921fb, 0x54442d18U) +#define D_3PI_4 PACK3(0x400, 0x2d97c, 0x7f3321d2U) +#define D_PI PACK3(0x400, 0x921fb, 0x54442d18U) + +/* + * C99 specifies that complex numbers have the same representation as + * an array of two elements, where the first element is the real part + * and the second element is the imaginary part. + */ +typedef union { + float complex f; + union IEEEf2bits a[2]; +} float_complex; +typedef union { + double complex f; + union IEEEd2bits a[2]; +} double_complex; +typedef union { + long double complex f; + union IEEEl2bits a[2]; +} long_double_complex; +#define REALPART(z) ((z).a[0]) +#define IMAGPART(z) ((z).a[1]) + +typedef struct { + long_double_complex l; + double_complex d; + float_complex f; +} fpblock; + +/* + * The following is a formal copy of WG14/N1256 G.6. The executable functions are + * found via dlfunc() using the name. The arguments and expected result are strings + * defining the value(s). These strings optionally begin with '+' or '-', indicating + * that the value has that sign, if neither is specified, the value can have either + * sign. 'x' and 'y' represent any finite value (including zero if this is not + * separately specified). + * + * The exceptions and special conditions fields are currently ignored. + * + * The table is terminated by a NULL name. + */ +const struct test_c_c { + const char *tname; + const char *tare, *taim; /* arguments */ + const char *trre, *trim; /* expected result */ + const char *texc; /* exceptions */ + const char *spec; /* special conditions */ +} test_c_c[] = { + { "cacos", "0", "+0", "+π/2", "-0" }, + { "cacos", "0", "-0", "+π/2", "+0" }, + { "cacos", "0", "NaN", "+π/2", "NaN" }, + { "cacos", "+Inf", "+Inf", "+π/4", "-Inf" }, + { "cacos", "+Inf", "-Inf", "+π/4", "+Inf" }, + { "cacos", "-Inf", "+Inf", "+3π/4", "-Inf" }, + { "cacos", "-Inf", "-Inf", "+3π/4", "+Inf" }, + { "cacos", "Inf", "NaN", "NaN", "Inf" }, + { "cacos", "+Inf", "+y", "+0", "-Inf" }, + { "cacos", "+Inf", "-y", "+0", "+Inf" }, + { "cacos", "-Inf", "+y", "+π", "-Inf" }, + { "cacos", "-Inf", "-y", "+π", "+Inf" }, + { "cacos", "NaN", "+Inf", "NaN", "-Inf" }, + { "cacos", "NaN", "-Inf", "NaN", "+Inf" }, + { "cacos", "NaN", "NaN", "NaN", "NaN" }, + { "cacos", "NaN", "y", "NaN", "NaN", "[inv]" }, + { "cacos", "x", "+Inf", "+π/2", "-Inf" }, + { "cacos", "x", "-Inf", "+π/2", "+Inf" }, + { "cacos", "x", "NaN", "NaN", "NaN", "[inv]" }, + + { "cacosh", "0", "+0", "+0", "+π/2" }, + { "cacosh", "0", "-0", "+0", "-π/2" }, + { "cacosh", "+Inf", "+Inf", "+Inf", "+π/4" }, + { "cacosh", "+Inf", "-Inf", "+Inf", "-π/4" }, + { "cacosh", "-Inf", "+Inf", "+Inf", "+3π/4" }, + { "cacosh", "-Inf", "-Inf", "+Inf", "-3π/4" }, + { "cacosh", "Inf", "NaN", "+Inf", "NaN" }, + { "cacosh", "+Inf", "+y", "+Inf", "+0" }, + { "cacosh", "+Inf", "-y", "+Inf", "-0" }, + { "cacosh", "-Inf", "+y", "+Inf", "+π" }, + { "cacosh", "-Inf", "-y", "+Inf", "-π" }, + { "cacosh", "NaN", "Inf", "+Inf", "NaN" }, + { "cacosh", "NaN", "NaN", "NaN", "NaN" }, + { "cacosh", "NaN", "y", "NaN", "NaN", "[inv]" }, + { "cacosh", "x", "+Inf", "+Inf", "+π/2" }, + { "cacosh", "x", "-Inf", "+Inf", "-π/2" }, + { "cacosh", "x", "NaN", "NaN", "NaN", "[inv]" }, + + { "casin", "-0", "+0", "-0", "+0" }, + { "casin", "+0", "+0", "+0", "+0" }, + { "casin", "-0", "-0", "-0", "-0" }, + { "casin", "+0", "-0", "+0", "-0" }, + { "casin", "-Inf", "+Inf", "-π/4", "+Inf" }, + { "casin", "+Inf", "+Inf", "+π/4", "+Inf" }, + { "casin", "-Inf", "-Inf", "-π/4", "-Inf" }, + { "casin", "+Inf", "-Inf", "+π/4", "-Inf" }, + { "casin", "NaN", "+Inf", "NaN", "+Inf" }, + { "casin", "NaN", "-Inf", "NaN", "-Inf" }, + { "casin", "-y", "+Inf", "-0", "+Inf" }, + { "casin", "+y", "+Inf", "+0", "+Inf" }, + { "casin", "-y", "-Inf", "-0", "-Inf" }, + { "casin", "+y", "-Inf", "+0", "-Inf" }, + { "casin", "-0", "NaN", "-0", "NaN" }, + { "casin", "+0", "NaN", "+0", "NaN" }, + { "casin", "Inf", "NaN", "NaN", "Inf" }, + { "casin", "NaN", "NaN", "NaN", "NaN" }, + { "casin", "y", "NaN", "NaN", "NaN", "[inv]" }, + { "casin", "-Inf", "+x", "-π/2", "+Inf" }, + { "casin", "+Inf", "+x", "+π/2", "+Inf" }, + { "casin", "-Inf", "-x", "-π/2", "-Inf" }, + { "casin", "+Inf", "-x", "+π/2", "-Inf" }, + { "casin", "NaN", "x", "NaN", "NaN", "[inv]" }, + + { "casinh", "+0", "+0", "+0", "+0" }, + { "casinh", "+0", "-0", "+0", "-0" }, + { "casinh", "-0", "+0", "-0", "+0" }, + { "casinh", "-0", "-0", "-0", "-0" }, + { "casinh", "+Inf", "+Inf", "+Inf", "+π/4" }, + { "casinh", "+Inf", "-Inf", "+Inf", "-π/4" }, + { "casinh", "-Inf", "+Inf", "-Inf", "+π/4" }, + { "casinh", "-Inf", "-Inf", "-Inf", "-π/4" }, + { "casinh", "+Inf", "NaN", "+Inf", "NaN" }, + { "casinh", "-Inf", "NaN", "-Inf", "NaN" }, + { "casinh", "+Inf", "+y", "+Inf", "+0" }, + { "casinh", "+Inf", "-y", "+Inf", "-0" }, + { "casinh", "-Inf", "+y", "-Inf", "+0" }, + { "casinh", "-Inf", "-y", "-Inf", "-0" }, + { "casinh", "NaN", "+0", "NaN", "+0" }, + { "casinh", "NaN", "-0", "NaN", "-0" }, + { "casinh", "NaN", "Inf", "Inf", "NaN" }, + { "casinh", "NaN", "NaN", "NaN", "NaN" }, + { "casinh", "NaN", "y", "NaN", "NaN", "[inv]" }, + { "casinh", "+x", "+Inf", "+Inf", "+π/2" }, + { "casinh", "+x", "-Inf", "+Inf", "-π/2" }, + { "casinh", "-x", "+Inf", "-Inf", "+π/2" }, + { "casinh", "-x", "-Inf", "-Inf", "-π/2" }, + { "casinh", "x", "NaN", "NaN", "NaN", "[inv]" }, + + { "catan", "-0", "+0", "-0", "+0" }, + { "catan", "+0", "+0", "+0", "+0" }, + { "catan", "-0", "-0", "-0", "-0" }, + { "catan", "+0", "-0", "+0", "-0" }, + { "catan", "NaN", "+0", "NaN", "+0" }, + { "catan", "NaN", "-0", "NaN", "-0" }, + { "catan", "-0", "+1", "-0", "+Inf", "{div0}" }, + { "catan", "+0", "+1", "+0", "+Inf", "{div0}" }, + { "catan", "-0", "-1", "-0", "-Inf", "{div0}" }, + { "catan", "+0", "-1", "+0", "-Inf", "{div0}" }, + { "catan", "-Inf", "+Inf", "-π/2", "+0" }, + { "catan", "+Inf", "+Inf", "+π/2", "+0" }, + { "catan", "-Inf", "-Inf", "-π/2", "-0" }, + { "catan", "+Inf", "-Inf", "+π/2", "-0" }, + { "catan", "NaN", "+Inf", "NaN", "+0" }, + { "catan", "NaN", "-Inf", "NaN", "-0" }, + { "catan", "-y", "+Inf", "-π/2", "+0" }, + { "catan", "+y", "+Inf", "+π/2", "+0" }, + { "catan", "-Inf", "NaN", "-π/2", "0" }, + { "catan", "+Inf", "NaN", "+π/2", "0" }, + { "catan", "NaN", "NaN", "NaN", "NaN" }, + { "catan", "y", "NaN", "NaN", "NaN", "[inv]" }, + { "catan", "-Inf", "+x", "-π/2", "+0" }, + { "catan", "+Inf", "+x", "+π/2", "+0" }, + { "catan", "-Inf", "-x", "-π/2", "-0" }, + { "catan", "+Inf", "-x", "+π/2", "-0" }, + { "catan", "NaN", "x", "NaN", "NaN", "[inv]" }, + + { "catanh", "+0", "+0", "+0", "+0" }, + { "catanh", "+0", "-0", "+0", "-0" }, + { "catanh", "-0", "+0", "-0", "+0" }, + { "catanh", "-0", "-0", "-0", "-0" }, + { "catanh", "+0", "NaN", "+0", "NaN" }, + { "catanh", "-0", "NaN", "-0", "NaN" }, + { "catanh", "+1", "+0", "+Inf", "+0", "{div0}" }, + { "catanh", "+1", "-0", "+Inf", "-0", "{div0}" }, + { "catanh", "-1", "+0", "-Inf", "+0", "{div0}" }, + { "catanh", "-1", "-0", "-Inf", "-0", "{div0}" }, + { "catanh", "+Inf", "+Inf", "+0", "+π/2" }, + { "catanh", "+Inf", "-Inf", "+0", "-π/2" }, + { "catanh", "-Inf", "+Inf", "-0", "+π/2" }, + { "catanh", "-Inf", "-Inf", "-0", "-π/2" }, + { "catanh", "+Inf", "NaN", "+0", "NaN" }, + { "catanh", "-Inf", "NaN", "-0", "NaN" }, + { "catanh", "+Inf", "+y", "+0", "+π/2" }, + { "catanh", "+Inf", "-y", "+0", "-π/2" }, + { "catanh", "NaN", "+Inf", "0", "+π/2" }, + { "catanh", "NaN", "-Inf", "0", "-π/2" }, + { "catanh", "NaN", "NaN", "NaN", "NaN" }, + { "catanh", "NaN", "y", "NaN", "NaN", "[inv]" }, + { "catanh", "+x", "+Inf", "+0", "+π/2" }, + { "catanh", "+x", "-Inf", "+0", "-π/2" }, + { "catanh", "-x", "+Inf", "-0", "+π/2" }, + { "catanh", "-x", "-Inf", "-0", "-π/2" }, + { "catanh", "x", "NaN", "NaN", "NaN", "[inv]" }, + + { "ccos", "-0", "+0", "+1", "+0" }, + { "ccos", "+0", "+0", "+1", "-0" }, + { "ccos", "-0", "-0", "+1", "-0" }, + { "ccos", "+0", "-0", "+1", "+0" }, + { "ccos", "Inf", "0", "NaN", "0", "{inv}" }, + { "ccos", "NaN", "0", "NaN", "0" }, + { "ccos", "-0", "+Inf", "+Inf", "+0" }, + { "ccos", "+0", "+Inf", "+Inf", "-0" }, + { "ccos", "-0", "-Inf", "+Inf", "-0" }, + { "ccos", "+0", "-Inf", "+Inf", "+0" }, + { "ccos", "Inf", "Inf", "Inf", "NaN", "{inv}" }, + { "ccos", "NaN", "Inf", "+Inf", "NaN" }, + { "ccos", "x", "+Inf", "+Inf*cos(x)", "-Inf*sin(x)" }, + { "ccos", "x", "-Inf", "+Inf*cos(x)", "+Inf*sin(x)" }, + { "ccos", "0", "NaN", "NaN", "0" }, + { "ccos", "NaN", "NaN", "NaN", "NaN" }, + { "ccos", "y", "NaN", "NaN", "NaN", "[inv]" }, + { "ccos", "Inf", "x", "NaN", "NaN", "{inv}" }, + { "ccos", "NaN", "x", "NaN", "NaN", "[inv]" }, + + { "ccosh", "+0", "+0", "+1", "+0" }, + { "ccosh", "-0", "+0", "+1", "-0" }, + { "ccosh", "+0", "-0", "+1", "-0" }, + { "ccosh", "-0", "-0", "+1", "+0" }, + { "ccosh", "0", "Inf", "NaN", "0", "{inv}" }, + { "ccosh", "0", "NaN", "NaN", "0" }, + { "ccosh", "+Inf", "+0", "+Inf", "+0" }, + { "ccosh", "+Inf", "-0", "+Inf", "-0" }, + { "ccosh", "-Inf", "+0", "+Inf", "-0" }, + { "ccosh", "-Inf", "-0", "+Inf", "+0" }, + { "ccosh", "Inf", "Inf", "Inf", "NaN", "{inv}" }, + { "ccosh", "Inf", "NaN", "+Inf", "NaN" }, + { "ccosh", "+Inf", "y", "+Inf*cos(y)", "+Inf*sin(y)" }, + { "ccosh", "-Inf", "y", "+Inf*cos(y)", "-Inf*sin(y)" }, + { "ccosh", "NaN", "0", "NaN", "0" }, + { "ccosh", "NaN", "NaN", "NaN", "NaN" }, + { "ccosh", "NaN", "y", "NaN", "NaN", "[inv]" }, + { "ccosh", "x", "Inf", "NaN", "NaN", "{inv}" }, + { "ccosh", "x", "NaN", "NaN", "NaN", "[inv]" }, + + { "cexp", "0", "+0", "+1", "+0" }, + { "cexp", "0", "-0", "+1", "-0" }, + { "cexp", "+Inf", "+0", "+Inf", "+0" }, + { "cexp", "+Inf", "-0", "+Inf", "-0" }, + { "cexp", "+Inf", "Inf", "Inf", "NaN", "{inv}" }, + { "cexp", "-Inf", "Inf", "0", "0" }, + { "cexp", "+Inf", "NaN", "Inf", "NaN" }, + { "cexp", "-Inf", "NaN", "0", "0" }, + { "cexp", "+Inf", "y", "+Inf*cos(y)", "+Inf*sin(y)" }, + { "cexp", "-Inf", "y", "+0*cos(y)", "+0*sin(y)" }, + { "cexp", "NaN", "+0", "NaN", "+0" }, + { "cexp", "NaN", "-0", "NaN", "-0" }, + { "cexp", "NaN", "NaN", "NaN", "NaN" }, + { "cexp", "NaN", "y", "NaN", "NaN", "[inv]" }, + { "cexp", "x", "Inf", "NaN", "NaN", "{inv}" }, + { "cexp", "x", "NaN", "NaN", "NaN", "[inv]" }, + + { "clog", "+0", "+0", "-Inf", "+0", "{div0}" }, + { "clog", "+0", "-0", "-Inf", "-0", "{div0}" }, + { "clog", "-0", "+0", "-Inf", "+π", "{div0}" }, + { "clog", "-0", "-0", "-Inf", "-π", "{div0}" }, + { "clog", "+Inf", "+Inf", "+Inf", "+π/4" }, + { "clog", "+Inf", "-Inf", "+Inf", "-π/4" }, + { "clog", "-Inf", "+Inf", "+Inf", "+3π/4" }, + { "clog", "-Inf", "-Inf", "+Inf", "-3π/4" }, + { "clog", "Inf", "NaN", "+Inf", "NaN" }, + { "clog", "+Inf", "+y", "+Inf", "+0" }, + { "clog", "+Inf", "-y", "+Inf", "-0" }, + { "clog", "-Inf", "+y", "+Inf", "+π" }, + { "clog", "-Inf", "-y", "+Inf", "-π" }, + { "clog", "NaN", "Inf", "+Inf", "NaN" }, + { "clog", "NaN", "NaN", "NaN", "NaN" }, + { "clog", "NaN", "y", "NaN", "NaN", "[inv]" }, + { "clog", "x", "+Inf", "+Inf", "+π/2" }, + { "clog", "x", "-Inf", "+Inf", "-π/2" }, + { "clog", "x", "NaN", "NaN", "NaN", "[inv]" }, + + { "csin", "-0", "+0", "-0", "+0" }, + { "csin", "+0", "+0", "+0", "+0" }, + { "csin", "-0", "-0", "-0", "-0" }, + { "csin", "+0", "-0", "+0", "-0" }, + { "csin", "Inf", "0", "NaN", "0", "{inv}" }, + { "csin", "NaN", "0", "NaN", "0" }, + { "csin", "-0", "+Inf", "-0", "+Inf" }, + { "csin", "+0", "+Inf", "+0", "+Inf" }, + { "csin", "-0", "-Inf", "-0", "-Inf" }, + { "csin", "+0", "-Inf", "+0", "-Inf" }, + { "csin", "Inf", "Inf", "NaN", "Inf", "{inv}" }, + { "csin", "NaN", "Inf", "NaN", "Inf" }, + { "csin", "x", "+Inf", "+Inf*sin(x)", "+Inf*cos(x)" }, + { "csin", "x", "-Inf", "+Inf*sin(x)", "-Inf*cos(x)" }, + { "csin", "-0", "NaN", "-0", "NaN" }, + { "csin", "+0", "NaN", "+0", "NaN" }, + { "csin", "NaN", "NaN", "NaN", "NaN" }, + { "csin", "y", "NaN", "NaN", "NaN", "[inv]" }, + { "csin", "Inf", "x", "NaN", "NaN", "{inv}" }, + { "csin", "NaN", "x", "NaN", "NaN", "[inv]" }, + + { "csinh", "+0", "+0", "+0", "+0" }, + { "csinh", "+0", "-0", "+0", "-0" }, + { "csinh", "-0", "+0", "-0", "+0" }, + { "csinh", "-0", "-0", "-0", "-0" }, + { "csinh", "0", "Inf", "0", "NaN", "{inv}" }, + { "csinh", "0", "NaN", "0", "NaN" }, + { "csinh", "+Inf", "+0", "+Inf", "+0" }, + { "csinh", "+Inf", "-0", "+Inf", "-0" }, + { "csinh", "-Inf", "+0", "-Inf", "+0" }, + { "csinh", "-Inf", "-0", "-Inf", "-0" }, + { "csinh", "Inf", "Inf", "Inf", "NaN", "{inv}" }, + { "csinh", "Inf", "NaN", "Inf", "NaN" }, + { "csinh", "+Inf", "y", "+Inf*cos(y)", "+Inf*sin(y)" }, + { "csinh", "-Inf", "y", "-Inf*cos(y)", "+Inf*sin(y)" }, + { "csinh", "NaN", "+0", "NaN", "+0" }, + { "csinh", "NaN", "-0", "NaN", "-0" }, + { "csinh", "NaN", "NaN", "NaN", "NaN" }, + { "csinh", "NaN", "y", "NaN", "NaN", "[inv]" }, + { "csinh", "x", "Inf", "NaN", "NaN", "{inv}" }, + { "csinh", "x", "NaN", "NaN", "NaN", "[inv]" }, + + { "csqrt", "0", "+0", "+0", "+0" }, + { "csqrt", "0", "-0", "+0", "-0" }, + { "csqrt", "+Inf", "NaN", "+Inf", "NaN" }, + { "csqrt", "-Inf", "NaN", "NaN", "Inf" }, + { "csqrt", "+Inf", "+y", "+Inf", "+0" }, + { "csqrt", "+Inf", "-y", "+Inf", "-0" }, + { "csqrt", "-Inf", "+y", "+0", "+Inf" }, + { "csqrt", "-Inf", "-y", "+0", "-Inf" }, + { "csqrt", "NaN", "+Inf", "+Inf", "+Inf" }, + { "csqrt", "NaN", "-Inf", "+Inf", "-Inf" }, + { "csqrt", "NaN", "NaN", "NaN", "NaN" }, + { "csqrt", "NaN", "y", "NaN", "NaN", "[inv]" }, + { "csqrt", "x", "+Inf", "+Inf", "+Inf" }, + { "csqrt", "x", "-Inf", "+Inf", "-Inf" }, + { "csqrt", "x", "NaN", "NaN", "NaN", "[inv]" }, + + { "ctan", "-0", "+0", "-0", "+0" }, + { "ctan", "+0", "+0", "+0", "+0" }, + { "ctan", "-0", "-0", "-0", "-0" }, + { "ctan", "+0", "-0", "+0", "-0" }, + { "ctan", "Inf", "+Inf", "0", "+1" }, + { "ctan", "Inf", "-Inf", "0", "-1" }, + { "ctan", "NaN", "+Inf", "0", "+1" }, + { "ctan", "NaN", "-Inf", "0", "-1" }, + { "ctan", "x", "+Inf", "+0*sin(2*x)", "+1" }, + { "ctan", "x", "-Inf", "+0*sin(2*x)", "-1" }, + { "ctan", "-0", "NaN", "-0", "NaN" }, + { "ctan", "+0", "NaN", "+0", "NaN" }, + { "ctan", "NaN", "NaN", "NaN", "NaN" }, + { "ctan", "y", "NaN", "NaN", "NaN", "[inv]" }, + { "ctan", "Inf", "x", "NaN", "NaN", "{inv}" }, + { "ctan", "NaN", "x", "NaN", "NaN", "[inv]" }, + + { "ctanh", "+0", "+0", "+0", "+0" }, + { "ctanh", "+0", "-0", "+0", "-0" }, + { "ctanh", "-0", "+0", "-0", "+0" }, + { "ctanh", "-0", "-0", "-0", "-0" }, + { "ctanh", "+Inf", "Inf", "+1", "0" }, + { "ctanh", "-Inf", "Inf", "-1", "0" }, + { "ctanh", "+Inf", "NaN", "+1", "0" }, + { "ctanh", "-Inf", "NaN", "-1", "0" }, + { "ctanh", "+Inf", "y", "+1", "+0*sin(2*y)" }, + { "ctanh", "-Inf", "y", "-1", "+0*sin(2*y)" }, + { "ctanh", "NaN", "+0", "NaN", "+0" }, + { "ctanh", "NaN", "-0", "NaN", "-0" }, + { "ctanh", "NaN", "NaN", "NaN", "NaN" }, + { "ctanh", "NaN", "y", "NaN", "NaN", "[inv]" }, + { "ctanh", "x", "Inf", "NaN", "NaN", "{inv}" }, + { "ctanh", "x", "NaN", "NaN", "NaN", "[inv]" }, + + { NULL } +}; + +/* argument/result string */ +enum T_VAL { + T_ZERO, T_FIN, T_INF, T_NAN, T_ONE, T_PI_4, T_PI_2, T_3PI_4, T_PI, + T_COSX, T_COSY, T_SINX, T_SIN2X, T_SINY, T_SIN2Y, T_OINV, T_DIV0, T_INV, T_UNK }; + +/* Used to control state for iterating argument values */ +enum STATE { + S_COUNT = 32768, S_SSIN2X, S_SSINX, S_SSIN2Y, S_SSINY, S_SCOSX, S_SCOSY, S_NAN, + S_MASK = 0xffff, S_NEG = 0x80000, S_POS = 0x40000, S_MULT = 0x20000 }; + +/* Map string name to enum and bit pattern of value */ +struct t_val { + const char *v_name; /* String name */ + enum T_VAL v_val; /* Value */ + fbits v_fval; /* float constant */ + dbits v_dval; /* double constant */ + lbits v_lval; /* long double constant */ +}; + +/* Convert the name (without sign) to the constants */ +const struct t_val * +mapname(const char *in) +{ + static const struct t_val t_map[] = { + { "0", T_ZERO, F_ZERO, D_ZERO, L_ZERO }, + { "0*cos(y)", T_COSY, F_ZERO, D_ZERO, L_ZERO }, + { "0*sin(2*y)", T_SIN2Y,F_ZERO, D_ZERO, L_ZERO }, + { "0*sin(y)", T_SINY, F_ZERO, D_ZERO, L_ZERO }, + { "0*cos(x)", T_COSX, F_ZERO, D_ZERO, L_ZERO }, + { "0*sin(2*x)", T_SIN2X,F_ZERO, D_ZERO, L_ZERO }, + { "0*sin(x)", T_SINX, F_ZERO, D_ZERO, L_ZERO }, + { "1", T_ONE, F_ONE, D_ONE, L_ONE }, + { "3π/4", T_3PI_4,F_3PI_4,D_3PI_4,L_3PI_4 }, + { "Inf", T_INF, F_INF, D_INF, L_INF }, + { "Inf*cos(y)", T_COSY, F_INF, D_INF, L_INF }, + { "Inf*sin(y)", T_SINY, F_INF, D_INF, L_INF }, + { "Inf*cos(x)", T_COSX, F_INF, D_INF, L_INF }, + { "Inf*sin(x)", T_SINX, F_INF, D_INF, L_INF }, + { "NaN", T_NAN, F_NAN, D_NAN, L_NAN }, + { "[inv]", T_OINV }, + { "x", T_FIN, F_3PI_4,D_3PI_4,L_3PI_4 }, + { "y", T_FIN, F_3PI_4,D_3PI_4,L_3PI_4 }, + { "{div0}", T_DIV0 }, + { "{inv}", T_INV }, + { "π", T_PI, F_PI, D_PI, L_PI }, + { "π/2", T_PI_2, F_PI_2, D_PI_2, L_PI_2 }, + { "π/4", T_PI_4, F_PI_4, D_PI_4, L_PI_4 }, + { NULL, T_UNK } + }; + int i; + + for (i = 0; t_map[i].v_name != NULL; i++) { + if (strcmp(t_map[i].v_name, in) == 0) + break; + } + return (&t_map[i]); +} + +/* + * vdecode() and do_validate() form partial sanity checks on the arguments + * and results in test_c_c + */ +int +vdecode(const char *name, const char *n, int isarg) +{ + int sign; + int val; + int res; + + sign = (*n == '+' || *n == '-') ? *n++ : 0; + val = mapname(n)->v_val; + if (val > (isarg ? T_NAN : T_SIN2Y)) { + printf("%s invalid %s '%s'\n", name, (isarg ? "arg" : "res"), n - !!sign); + return (0); + } + if (!isarg) + return (0); + res = 0; + if (sign != '-') + res |= val + 1; + if (sign != '+') + res |= (val + 5) << 8; + return (res); +} + +/* validate test_c_c[] contents */ +void +do_validate() +{ + const struct test_c_c *ep, *sp; + int res1, res2, res3, res4; + int64_t mask, val; + + if (sizeof(float) != sizeof(struct Ieeef2bits) || sizeof(float) != sizeof(union IEEEf2bits) || sizeof(float complex) != sizeof(float_complex)) + printf("float: %zu %zu %zu %zu %zu\n", sizeof(float), sizeof(struct Ieeef2bits), sizeof(union IEEEf2bits), sizeof(float complex), sizeof(float_complex)); + if (sizeof(double) != sizeof(struct Ieeed2bits) || sizeof(double) != sizeof(union IEEEd2bits) || sizeof(double complex) != sizeof(double_complex)) + printf("double: %zu %zu %zu %zu %zu\n", sizeof(double), sizeof(struct Ieeed2bits), sizeof(union IEEEd2bits), sizeof(double complex), sizeof(double_complex)); + if (sizeof(long double) != sizeof(struct Ieeel2bits) || sizeof(long double) != sizeof(union IEEEl2bits) || sizeof(long double complex) != sizeof(long_double_complex)) + printf("long double: %zu %zu %zu %zu %zu\n", sizeof(long double), sizeof(struct Ieeel2bits), sizeof(union IEEEl2bits), sizeof(long double complex), sizeof(long_double_complex)); + + for (ep = test_c_c; ep->tname != NULL; ) { + mask = 0; + for (sp = ep; ep->tname != NULL && !strcmp(ep->tname, sp->tname); ep++) { + res1 = vdecode(ep->tname, ep->tare, 1); + res3 = res1 >> 8; + res1 &= 0xff; + res2 = vdecode(ep->tname, ep->taim, 2); + res4 = res2 >> 8; + res2 &= 0xff; + vdecode(ep->tname, ep->trre, 0); + vdecode(ep->tname, ep->trim, 0); + val = 0; + if (res1 > 0) { + if (res2 > 0) + val |= (int64_t)1 << (res1 + res2 * 8 - 9); + if (res4 > 0) + val |= (int64_t)1 << (res1 + res4 * 8 - 9); + } + if (res3 > 0) { + if (res2 > 0) + val |= (int64_t)1 << (res3 + res2 * 8 - 9); + if (res4 > 0) + val |= (int64_t)1 << (res3 + res4 * 8 - 9); + } + if (val & mask) + printf("%s duplicate '%s,%s' 0x%016jx 0x%016jx %x %x %x %x\n", + ep->tname, ep->tare, ep->taim, (intmax_t)mask, (intmax_t)val, res1, res3, res2, res4); + mask |= val; + } + } +} + +/* + * Lookup an argument or result string and initialise the state and value (in fp). + * + * n is the input string describing the argument or expectud result. + * state is used to return state information used to iterate through + * arguments or define result information, based on enum STATE + * fp is used to return the value associated with the input string. It contains + * float, double and long double complex numbers. A single invocation of + * decode will initialise either the real or imaginary parts of all 3. + * ixexp - zero if the input string is an argument, non-zero for result. + * ix is 0 for the real part or 1 for the imaginary part. + */ +int +decode(const char *n, int *state, fpblock *fp, int isexp, int ix) +{ + const struct t_val *val; + int sign; + + + sign = (*n == '+' || *n == '-') ? *n++ : 0; + val = mapname(n); + + fp->f.a[ix].bits = val->v_fval; + fp->d.a[ix].bits = val->v_dval; + fp->l.a[ix].bits = val->v_lval; + + /* Reject values not allowed as arguments */ + if (isexp == 0) { + switch (val->v_val) { + case T_FIN: + *state = S_MULT | S_COUNT | + ((sign == '+') ? S_POS : 0) | + ((sign == '-') ? S_NEG : 0); + if (sign == '-') { + fp->f.a[ix].bits.sign = 1; + fp->d.a[ix].bits.sign = 1; + fp->l.a[ix].bits.sign = 1; + } + return (0); + + case T_NAN: + *state = S_MASK; /*#### expand to more NaNs later */ + return (0); + + case T_ZERO: + case T_INF: + case T_ONE: + break; + + default: + return (1); + } + + switch (sign) { + case '-': + fp->f.a[ix].bits.sign = 1; + fp->d.a[ix].bits.sign = 1; + fp->l.a[ix].bits.sign = 1; + /* Fall thru */ + case '+': + *state = S_MASK; + break; + default: + *state = S_NEG; + break; + } + return (0); + } + + /* Process expected results */ + switch (val->v_val) { + case T_FIN: + case T_ZERO: + case T_INF: + case T_ONE: + case T_3PI_4: + case T_PI: + case T_PI_2: + case T_PI_4: + *state = 0; + break; + + case T_NAN: + *state = S_NAN; + return (0); + + case T_COSX: + *state = S_SCOSX; + break; + + case T_COSY: + *state = S_SCOSY; + break; + + case T_SIN2X: + *state = S_SSIN2X; + break; + + case T_SINX: + *state = S_SSINX; + break; + + case T_SIN2Y: + *state = S_SSIN2Y; + break; + + case T_SINY: + *state = S_SSINY; + break; + + case T_OINV: + case T_DIV0: + case T_INV: + case T_UNK: + default: + return (1); + } + + switch (sign) { + case '-': + fp->f.a[ix].bits.sign = 1; + fp->d.a[ix].bits.sign = 1; + fp->l.a[ix].bits.sign = 1; + break; + + case '+': + break; + + default: + *state |= S_NEG; + break; + } + + return (0); +} + +/* + * Verify the real or imaginary part (defined by "ix") of a return value + * (in "act") matches the expected value (in "exp") for an input value "arg" + * in the given state. check() needs to be called twice (with the results of + * each or'd together) to check a complex result. "act" is forced positive + * so that printing it with %e does not include a sign. The actual sign is + * returned as part of the return value. + * + * Returns a variety of information as a bit pattern: + * 00000007 - Value mismatch bits - L/D/F + * 00000070 - Sign (0 == -ve) of the real L/D/F actual results + * 00000700 - Sign (0 == -ve) of the imaginary L/D/F actual results + * 00077000 - Pairs of bits (LLDDFF) indicating sign to be printed for the expected real result + * 07700000 - Pairs of bits (LLDDFF) indicating sign to be printed for the expected imaginary result + * For the latter two, bitpairs are an index into thu string "??+-" + */ +int +check(const fpblock *arg, int ix, fpblock *act, const fpblock *exx, int state) +{ + int sf, sd, sl; + int asign, xsign; + + asign = (act->f.a[ix].bits.sign << 3) | + (act->d.a[ix].bits.sign << 4) | + (act->l.a[ix].bits.sign << 5); + if (ix) + asign <<= 3; + + /* Special case NaN */ + if (state == S_NAN) { + return ((isnan(act->f.a[ix].f) ? 0 : 1) | + (isnan(act->d.a[ix].f) ? 0 : 2) | + (isnan(act->l.a[ix].f) ? 0 : 4) | + asign); + } + + switch (state & S_MASK) { + default: + sf = sd = sl = 0; + break; + + case S_SSIN2X: + sf = signbit(sinf(arg->f.a[0].f * 2.0f)); + sd = signbit(sin(arg->d.a[0].f * 2.0)); + sl = signbit(sinl(arg->l.a[0].f * 2.0l)); + break; + + case S_SSINX: + sf = signbit(sinf(arg->f.a[0].f)); + sd = signbit(sin(arg->d.a[0].f)); + sl = signbit(sinl(arg->l.a[0].f)); + break; + + case S_SSIN2Y: + sf = signbit(sinf(arg->f.a[1].f * 2.0f)); + sd = signbit(sin(arg->d.a[1].f * 2.0)); + sl = signbit(sinl(arg->l.a[1].f * 2.0l)); + break; + + case S_SSINY: + sf = signbit(sinf(arg->f.a[1].f)); + sd = signbit(sin(arg->d.a[1].f)); + sl = signbit(sinl(arg->l.a[1].f)); + break; + + case S_SCOSX: + sf = signbit(cosf(arg->f.a[0].f)); + sd = signbit(cos(arg->d.a[0].f)); + sl = signbit(cosl(arg->l.a[0].f)); + break; + + case S_SCOSY: + sf = signbit(cosf(arg->f.a[1].f)); + sd = signbit(cos(arg->d.a[1].f)); + sl = signbit(cosl(arg->l.a[1].f)); + break; + } + + /* Ignore sign bit if we don't care */ + if (state & S_NEG) { + act->f.a[ix].bits.sign = 0; + act->d.a[ix].bits.sign = 0; + act->l.a[ix].bits.sign = 0; + xsign = 0; + } else { + /* + * s{f,d,l} indicates whether the signbit in the expected value should be + * inverted before comparison. To avoid altering it, flip the "actual" + * bit instead. The original signs are safely stored in asign. + */ + if (sf) + act->f.a[ix].bits.sign = ~act->f.a[ix].bits.sign; + if (sd) + act->d.a[ix].bits.sign = ~act->d.a[ix].bits.sign; + if (sl) + act->l.a[ix].bits.sign = ~act->l.a[ix].bits.sign; + + /* Calculate the sign to be displayed for the expected result */ + xsign = 00052000 | + ((!!sf ^ exx->f.a[ix].bits.sign) << 9) | + ((!!sd ^ exx->d.a[ix].bits.sign) << 11) | + ((!!sl ^ exx->l.a[ix].bits.sign) << 13); + if (ix) + xsign <<= 6; + } *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***help
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201306170849.r5H8n89J001608>
