
///////////////////////////////////////////////////////////////////////////////
//
// File   : Asura_HalfFloat.h
// Author : Andy Tate
// Date   : 22-Sep-2005
//
// Copyright (C) 2005 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////
//
// Representation of a float:
//
//	We assume that a float, f, is an IEEE 754 single-precision
//	floating point number, whose bits are arranged as follows:
//
//	    31 (msb)
//	    | 
//	    | 30     23
//	    | |      | 
//	    | |      | 22                    0 (lsb)
//	    | |      | |                     |
//	    X XXXXXXXX XXXXXXXXXXXXXXXXXXXXXXX
//
//	    s e        m
//
//	S is the sign-bit, e is the exponent and m is the significand.
//
//	If e is between 1 and 254, f is a normalized number:
//
//	            s    e-127
//	    f = (-1)  * 2      * 1.m
//
//	If e is 0, and m is not zero, f is a denormalized number:
//
//	            s    -126
//	    f = (-1)  * 2      * 0.m
//
//	If e and m are both zero, f is zero:
//
//	    f = 0.0
//
//	If e is 255, f is an "infinity" or "not a number" (NAN),
//	depending on whether m is zero or not.
//
//	Examples:
//
//	    0 00000000 00000000000000000000000 = 0.0
//	    0 01111110 00000000000000000000000 = 0.5
//	    0 01111111 00000000000000000000000 = 1.0
//	    0 10000000 00000000000000000000000 = 2.0
//	    0 10000000 10000000000000000000000 = 3.0
//	    1 10000101 11110000010000000000000 = -124.0625
//	    0 11111111 00000000000000000000000 = +infinity
//	    1 11111111 00000000000000000000000 = -infinity
//	    0 11111111 10000000000000000000000 = NAN
//	    1 11111111 11111111111111111111111 = NAN
//
///////////////////////////////////////////////////////////////////////////////
//
// Representation of a half:
//
//	Here is the bit-layout for a half number, h:
//
//	    15 (msb)
//	    | 
//	    | 14  10
//	    | |   |
//	    | |   | 9        0 (lsb)
//	    | |   | |        |
//	    X XXXXX XXXXXXXXXX
//
//	    s e     m
//
//	S is the sign-bit, e is the exponent and m is the significand.
//
//	If e is between 1 and 30, h is a normalized number:
//
//	            s    e-15
//	    h = (-1)  * 2     * 1.m
//
//	If e is 0, and m is not zero, h is a denormalized number:
//
//	            S    -14
//	    h = (-1)  * 2     * 0.m
//
//	If e and m are both zero, h is zero:
//
//	    h = 0.0
//
//	If e is 31, h is an "infinity" or "not a number" (NAN),
//	depending on whether m is zero or not.
//
//	Examples:
//
//	    0 00000 0000000000 = 0.0
//	    0 01110 0000000000 = 0.5
//	    0 01111 0000000000 = 1.0
//	    0 10000 0000000000 = 2.0
//	    0 10000 1000000000 = 3.0
//	    1 10101 1111000001 = -124.0625
//	    0 11111 0000000000 = +infinity
//	    1 11111 0000000000 = -infinity
//	    0 11111 1000000000 = NAN
//	    1 11111 1111111111 = NAN
//
///////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
	#pragma once
#endif   // ifdef ASURA_USE_PRAGMA_ONCE

#ifndef __ASURA_HALFFLOAT_H__
#define __ASURA_HALFFLOAT_H__

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Forward class declarations...
////////////////////////////////////////////////////////////////////////////////

//#define ASURA_FORCE_HALF_TO_FLOAT	(1)


////////////////////////////////////////////////////////////////////////////////
// Class declaration...
////////////////////////////////////////////////////////////////////////////////

class Asura_HalfFloat
{
	// Friend classes...
	friend class Asura_Chunk_Stream;


public:
	// Public functions...

	Asura_HalfFloat  (void)															{}
	Asura_HalfFloat  (const float fValue)											{ uUIntFloat xUnion; xUnion.f = fValue; m_usBits = FloatToHalf (xUnion.u); }
	Asura_HalfFloat  (const Asura_HalfFloat& xOther)	: m_usBits(xOther.m_usBits)	{}
	Asura_HalfFloat  (const u_short usBits)	: m_usBits(usBits)						{}
	~Asura_HalfFloat ()																{}

	// Assignment operators...
	Asura_HalfFloat&	operator=		(const float fValue)			{ uUIntFloat xUnion; xUnion.f = fValue; m_usBits = FloatToHalf (xUnion.u); return (*this); }
	Asura_HalfFloat&	operator=		(const Asura_HalfFloat xOther)	{ m_usBits = xOther.m_usBits; return (*this); }

	Asura_HalfFloat&	operator+=		(const Asura_HalfFloat xOther)	{ *this = *this + xOther; return (*this); }
	Asura_HalfFloat&	operator-=		(const Asura_HalfFloat xOther)	{ *this = *this - xOther; return (*this); }
	Asura_HalfFloat&	operator*=		(const Asura_HalfFloat xOther)	{ *this = *this * xOther; return (*this); }
	Asura_HalfFloat&	operator/=		(const Asura_HalfFloat xOther)	{ *this = *this / xOther; return (*this); }

	// Unary minus operator...
	Asura_HalfFloat		operator-		(void) const					{ u_short usBits = m_usBits ^ 0x8000; return (Asura_HalfFloat(usBits)); }

	// Conversion to float operator...
	operator float (void) const
	{
		uUIntFloat	xUnion;
		
		// Perform the conversion...
		xUnion.u	= HalfToFloat (m_usBits);
		
		// Return the floating-point equivalent...
		return	(xUnion.f);
	}

	// Operators...
	Asura_HalfFloat operator+ (const Asura_HalfFloat xOther)
	{
		uUIntFloat		xLHS;
		uUIntFloat		xRHS;
		uUIntFloat		xResult;
		Asura_HalfFloat	xReturn;

		// Convert both to floats...
		xLHS.u	= HalfToFloat (m_usBits);
		xRHS.u	= HalfToFloat (xOther.m_usBits);

		// Sum the 2 floats...
		xResult.f	= xLHS.f + xRHS.f;

		// Convert the result back to a half...
		xReturn.FloatToHalf	(xResult.u);
		return				(xReturn);
	}

	Asura_HalfFloat operator- (const Asura_HalfFloat xOther)
	{
		uUIntFloat		xLHS;
		uUIntFloat		xRHS;
		uUIntFloat		xResult;
		Asura_HalfFloat	xReturn;

		// Convert both to floats...
		xLHS.u	= HalfToFloat (m_usBits);
		xRHS.u	= HalfToFloat (xOther.m_usBits);

		// Subtract the 2 floats...
		xResult.f	= xLHS.f - xRHS.f;

		// Convert the result back to a half...
		xReturn.FloatToHalf	(xResult.u);
		return				(xReturn);
	}

	Asura_HalfFloat operator* (const Asura_HalfFloat xOther)
	{
		uUIntFloat		xLHS;
		uUIntFloat		xRHS;
		uUIntFloat		xResult;
		Asura_HalfFloat	xReturn;

		// Convert both to floats...
		xLHS.u	= HalfToFloat (m_usBits);
		xRHS.u	= HalfToFloat (xOther.m_usBits);

		// Multiply the 2 floats...
		xResult.f	= xLHS.f * xRHS.f;

		// Convert the result back to a half...
		xReturn.FloatToHalf	(xResult.u);
		return				(xReturn);
	}

	Asura_HalfFloat operator/ (const Asura_HalfFloat xOther)
	{
		uUIntFloat		xLHS;
		uUIntFloat		xRHS;
		uUIntFloat		xResult;
		Asura_HalfFloat	xReturn;

		// Convert both to floats...
		xLHS.u	= HalfToFloat (m_usBits);
		xRHS.u	= HalfToFloat (xOther.m_usBits);

		// Divide the 2 floats...
		xResult.f	= xLHS.f / xRHS.f;

		// Convert the result back to a half...
		xReturn.FloatToHalf	(xResult.u);
		return				(xReturn);
	}


protected:
	// Protected variables...


	// Protected functions...

	static u_short FloatToHalf (const u_int uInteger)
	{
		u_int	uSign		= (uInteger >> 16) & 0x00008000;
		int		iExponent	= ((uInteger >> 23) & 0x000000ff) - (127 - 15);
		u_int	uMantissa	= uInteger & 0x007fffff;

		// Check the range of the exponent...
		if (iExponent <= 0)
		{
			// Fractional - is it too small for our half?
			if (iExponent < -10)
			{
				// Yes - assume zero...
				return	(0);
			}

			else
			{
				// No - return the appropriate bit-pattern...
				uMantissa = (uMantissa | 0x00800000) >> (1 - iExponent);

				return	(static_cast<u_short>(uSign | (uMantissa >> 13)));
			}
		}

		else if (iExponent == (0xFF - (127 - 15)))
		{
			if (uMantissa == 0)
			{
				// Infinity...
				return	(static_cast<u_short>(uSign | 0x7C00));
			}

			else
			{
				// NaN...
				uMantissa	>>= 13;

				return	(static_cast<u_short>(uSign | 0x7C00 | uMantissa | (uMantissa == 0)));
			}
		}

		else
		{
			if (iExponent > 30)
			{
				// Overflow...
				return	(static_cast<u_short>(uSign | 0x7C00));
			}

			else
			{
				return	(static_cast<u_short>(uSign | (iExponent << 10) | (uMantissa >> 13)));
			}
		}
	}

	static u_int HalfToFloat (const u_short usInteger)
	{
		u_int	uSign		= (usInteger >> 15) & 0x00000001;
		u_int	uExponent	= (usInteger >> 10) & 0x0000001F;
		u_int	uMantissa	= usInteger & 0x000003FF;

		// Check the range of the exponent...
		if (uExponent == 0)
		{
			if (uMantissa == 0)
			{
				// Plus or minus zero...
				return	(uSign << 31);
			}

			else
			{
				// Denormalized number -- renormalize it...
				while ((uMantissa & 0x00000400) != 0)
				{
					uMantissa	<<= 1;
					uExponent	 -= 1;
				}

				uExponent	+= 1;
				uMantissa	&= ~0x00000400;
			}
		}

		else if (uExponent == 31)
		{
			if (uMantissa == 0)
			{
				// Infinity...
				return	((uSign << 31) | 0x7F800000);
			}

			else
			{
				// NaN...
				return	((uSign << 31) | 0x7F800000 | (uMantissa << 13));
			}
		}

		// Calculate the single-precision exponent and mantissa...
		uExponent	= uExponent + (127 - 15);
		uMantissa	= uMantissa << 13;

		// Combine everything back together...
		return	((uSign << 31) | (uExponent << 23) | uMantissa);
	}


private:
	// Private union...

    union uUIntFloat
    {
		u_int	u;
		float	f;
    };


	// Private variables...

	// Bit-pattern used for the half...
	u_short	m_usBits;


	// Private functions...


};   // End of class 'Asura_HalfFloat'.


#endif   // ifndef __ASURA_HALFFLOAT_H__


/// End of file. ///
