/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunPro, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice
 * is preserved.
 * ====================================================
 */

/*
 * scalbln(double x, long n)
 * scalbln(x,n) returns x * 2**n computed by exponent
 * manipulation rather than by actually performing an
 * exponentiation or a multiplication.
 */

#include "math.h"
#include "math_private.h"
#include <limits.h>

static const double
two54  = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */
twom54 = 5.55111512312578270212e-17, /* 0x3C900000, 0x00000000 */
huge   = 1.0e+300,
tiny   = 1.0e-300;

double scalbln(double x, long n)
{
	int32_t k, hx, lx;

	EXTRACT_WORDS(hx, lx, x);
	k = (hx & 0x7ff00000) >> 20; /* extract exponent */
	if (k == 0) { /* 0 or subnormal x */
		if ((lx | (hx & 0x7fffffff)) == 0)
			return x; /* +-0 */
		x *= two54;
		GET_HIGH_WORD(hx, x);
		k = ((hx & 0x7ff00000) >> 20) - 54;
	}
	if (k == 0x7ff)
		return x + x; /* NaN or Inf */
	k = k + n;
	if (k > 0x7fe)
		return huge * copysign(huge, x); /* overflow */
	if (n < -50000)
		return tiny * copysign(tiny, x); /* underflow */
	if (k > 0) { /* normal result */
		SET_HIGH_WORD(x, (hx & 0x800fffff) | (k << 20));
		return x;
	}
	if (k <= -54) {
		if (n > 50000) /* in case integer overflow in n+k */
			return huge * copysign(huge, x); /* overflow */
		return tiny * copysign(tiny, x); /* underflow */
	}
	k += 54; /* subnormal result */
	SET_HIGH_WORD(x, (hx & 0x800fffff) | (k << 20));
	return x * twom54;
}
libm_hidden_def(scalbln)

#if LONG_MAX == INT_MAX
/* strong_alias(scalbln, scalbn) - "error: conflicting types for 'scalbn'"
 * because it tries to declare "typeof(scalbln) scalbn;"
 * which tries to give "long" parameter to scalbn.
 * Doing it by hand:
 */
__typeof(scalbn) scalbn __attribute__((alias("scalbln")));
#else
double scalbn(double x, int n)
{
	return scalbln(x, n);
}
#endif
libm_hidden_def(scalbn)
