/* Beginning of modification history */
/* Written 02-04-10 by Paul Green (Paul.Green@stratus.com) */
/* End of modification history */

/* This test case is extracted from Perl version 5.7.3.  It is
   in the Perl_unpack_str function of the pp_pack.c source file.

   GCC 2.95.2 improperly assumes that it can compensate for an
   extra fsub by performing a fadd.  This would work in
   fixed-point arithmetic, but does not work in floating-point
   arithmetic.

   This problem has been seen on HP-UX and on Stratus VOS, both
   of which have an HP PA-RISC target (hppa1.1).  The Stratus
   bug number is gnu_g++-220.  */

/* #define _POSIX_C_SOURCE 199506L -- added by Configure */
#include <stdio.h>
#include <string.h>
#include <math.h>

void test(double *result)
{
	float	afloat;
	double	adouble;
	int		checksum = 0;
	unsigned	cuv = 0;
	double	cdouble = 0.0;
	const int	bits_in_uv = 8 * sizeof(cuv);

	checksum = 53;
	cdouble = -1.0;

	if (checksum) {
		if (checksum > bits_in_uv) {
			double trouble;

			adouble = (double) (1 << (checksum & 15));

			while (checksum >= 16) {
				checksum -= 16;
				adouble *= 65536.0;
			}

			/* At -O1, GCC 2.95.2 compiles the following loop
			   into:

			   L$0014
				fcmp,dbl,>= %fr4,%fr0
				ftest
				b L$0014
				fadd,dbl %fr4,%fr12,%fr4
				fsub,dbl %fr4,%fr12,%fr4

				This code depends on the floating-add and
				floating-subtract retaining all of the
				precision present in the operands.  There is
				no such guarantee when using floating-point,
				as this test case demonstrates.

				The code is okay at -O0.  */

			while (cdouble < 0.0)
				cdouble += adouble;

			cdouble = modf (cdouble / adouble, &trouble) * adouble;
		}
	}

	*result = cdouble;
}

int main (int argc, char ** argv)
{
double	value;

	test (&value);

	if (argc == 2 && !strcmp(argv[1],"-v"))
		printf ("value = %.18e\n", value);

	if (value != 9.007199254740991e+15) {
		printf ("t001 fails!\n");
		return -1;
	}
	else {
		printf ("t001 works.\n");
		return 0;
	}
}
