162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* IEEE754 floating point arithmetic
362306a36Sopenharmony_ci * single precision square root
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci/*
662306a36Sopenharmony_ci * MIPS floating point support
762306a36Sopenharmony_ci * Copyright (C) 1994-2000 Algorithmics Ltd.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "ieee754sp.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ciunion ieee754sp ieee754sp_sqrt(union ieee754sp x)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	int ix, s, q, m, t, i;
1562306a36Sopenharmony_ci	unsigned int r;
1662306a36Sopenharmony_ci	COMPXSP;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	/* take care of Inf and NaN */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	EXPLODEXSP;
2162306a36Sopenharmony_ci	ieee754_clearcx();
2262306a36Sopenharmony_ci	FLUSHXSP;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	/* x == INF or NAN? */
2562306a36Sopenharmony_ci	switch (xc) {
2662306a36Sopenharmony_ci	case IEEE754_CLASS_SNAN:
2762306a36Sopenharmony_ci		return ieee754sp_nanxcpt(x);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	case IEEE754_CLASS_QNAN:
3062306a36Sopenharmony_ci		/* sqrt(Nan) = Nan */
3162306a36Sopenharmony_ci		return x;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	case IEEE754_CLASS_ZERO:
3462306a36Sopenharmony_ci		/* sqrt(0) = 0 */
3562306a36Sopenharmony_ci		return x;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	case IEEE754_CLASS_INF:
3862306a36Sopenharmony_ci		if (xs) {
3962306a36Sopenharmony_ci			/* sqrt(-Inf) = Nan */
4062306a36Sopenharmony_ci			ieee754_setcx(IEEE754_INVALID_OPERATION);
4162306a36Sopenharmony_ci			return ieee754sp_indef();
4262306a36Sopenharmony_ci		}
4362306a36Sopenharmony_ci		/* sqrt(+Inf) = Inf */
4462306a36Sopenharmony_ci		return x;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	case IEEE754_CLASS_DNORM:
4762306a36Sopenharmony_ci	case IEEE754_CLASS_NORM:
4862306a36Sopenharmony_ci		if (xs) {
4962306a36Sopenharmony_ci			/* sqrt(-x) = Nan */
5062306a36Sopenharmony_ci			ieee754_setcx(IEEE754_INVALID_OPERATION);
5162306a36Sopenharmony_ci			return ieee754sp_indef();
5262306a36Sopenharmony_ci		}
5362306a36Sopenharmony_ci		break;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	ix = x.bits;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	/* normalize x */
5962306a36Sopenharmony_ci	m = (ix >> 23);
6062306a36Sopenharmony_ci	if (m == 0) {		/* subnormal x */
6162306a36Sopenharmony_ci		for (i = 0; (ix & 0x00800000) == 0; i++)
6262306a36Sopenharmony_ci			ix <<= 1;
6362306a36Sopenharmony_ci		m -= i - 1;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci	m -= 127;		/* unbias exponent */
6662306a36Sopenharmony_ci	ix = (ix & 0x007fffff) | 0x00800000;
6762306a36Sopenharmony_ci	if (m & 1)		/* odd m, double x to make it even */
6862306a36Sopenharmony_ci		ix += ix;
6962306a36Sopenharmony_ci	m >>= 1;		/* m = [m/2] */
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* generate sqrt(x) bit by bit */
7262306a36Sopenharmony_ci	ix += ix;
7362306a36Sopenharmony_ci	s = 0;
7462306a36Sopenharmony_ci	q = 0;			/* q = sqrt(x) */
7562306a36Sopenharmony_ci	r = 0x01000000;		/* r = moving bit from right to left */
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	while (r != 0) {
7862306a36Sopenharmony_ci		t = s + r;
7962306a36Sopenharmony_ci		if (t <= ix) {
8062306a36Sopenharmony_ci			s = t + r;
8162306a36Sopenharmony_ci			ix -= t;
8262306a36Sopenharmony_ci			q += r;
8362306a36Sopenharmony_ci		}
8462306a36Sopenharmony_ci		ix += ix;
8562306a36Sopenharmony_ci		r >>= 1;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (ix != 0) {
8962306a36Sopenharmony_ci		ieee754_setcx(IEEE754_INEXACT);
9062306a36Sopenharmony_ci		switch (ieee754_csr.rm) {
9162306a36Sopenharmony_ci		case FPU_CSR_RU:
9262306a36Sopenharmony_ci			q += 2;
9362306a36Sopenharmony_ci			break;
9462306a36Sopenharmony_ci		case FPU_CSR_RN:
9562306a36Sopenharmony_ci			q += (q & 1);
9662306a36Sopenharmony_ci			break;
9762306a36Sopenharmony_ci		}
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci	ix = (q >> 1) + 0x3f000000;
10062306a36Sopenharmony_ci	ix += (m << 23);
10162306a36Sopenharmony_ci	x.bits = ix;
10262306a36Sopenharmony_ci	return x;
10362306a36Sopenharmony_ci}
104