////////////////////////////////////////////////////////////////////
// HalfFoat.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)

#ifndef __SEACAVE_HALFFLOAT_H__
#define __SEACAVE_HALFFLOAT_H__


// I N C L U D E S /////////////////////////////////////////////////


// D E F I N E S ///////////////////////////////////////////////////


namespace SEACAVE {

// S T R U C T S ///////////////////////////////////////////////////


// hfloat - a 16-bit floating point number class
// 
// Can represent positive and negative numbers whose magnitude is between 
// roughly 6.1e-5 and 6.5e+4 with a relative error of 9.8e-4; numbers 
// smaller than 6.1e-5 can be represented with an absolute error of 
// 6.0e-8. All integers from -2048 to +2048 can be represented exactly.
// Supports Denormals-as-zero (DAZ), but does not support infinities or NaN.
//
// Only conversions from half to float are lossless.
class GENERAL_API hfloat
{
public:
	typedef short Type;

	inline hfloat() : val(fromFloat(float())) {}
	explicit inline hfloat(float v) : val(fromFloat(v)) {}
	template <typename T>
	inline hfloat(T v) : val(fromFloat(static_cast<float>(v))) {}

	inline hfloat& operator = (float v) {
		val = fromFloat(v);
		return *this;
	}
	template <typename T>
	inline hfloat& operator = (T v) {
		val = fromFloat(static_cast<float>(v));
		return *this;
	}

	inline operator float() const {
		return toFloat(val);
	}
	inline hfloat operator - () const {
		return fromFloat2HFloat(-toFloat(val));
	}
	inline hfloat operator + (hfloat v) {
		return fromFloat2HFloat(toFloat(val) + toFloat(v.val));
	}
	inline hfloat operator + (float v) {
		return fromFloat2HFloat(toFloat(val) + v);
	}
	inline hfloat& operator += (hfloat v) {
		return *this = *this + v;
	}
	inline hfloat& operator += (float v) {
		return *this = *this + v;
	}
	inline hfloat operator - (hfloat v) {
		return fromFloat2HFloat(toFloat(val) - toFloat(v.val));
	}
	inline hfloat operator - (float v) {
		return fromFloat2HFloat(toFloat(val) - v);
	}
	inline hfloat& operator -= (hfloat v) {
		return *this = *this - v;
	}
	inline hfloat& operator -= (float v) {
		return *this = *this - v;
	}
	inline hfloat operator * (hfloat v) {
		return fromFloat2HFloat(toFloat(val) * toFloat(v.val));
	}
	inline hfloat operator * (float v) {
		return fromFloat2HFloat(toFloat(val) * v);
	}
	inline hfloat& operator *= (hfloat v) {
		return *this = *this * v;
	}
	inline hfloat& operator *= (float v) {
		return *this = *this * v;
	}
	inline hfloat operator / (hfloat v) {
		return fromFloat2HFloat(toFloat(val) / toFloat(v.val));
	}
	inline hfloat operator / (float v) {
		return fromFloat2HFloat(toFloat(val) / v);
	}
	inline hfloat& operator /= (hfloat v) {
		return *this = *this / v;
	}
	inline hfloat& operator /= (float v) {
		return *this = *this / v;
	}

	static float min() { return 6.1e-5f; }
	static float max() { return 6.5e+4f; }

	static inline short fromFloat(float f) {
		ASSERT(ISFINITE(f) && (ABS(f) == 0.f || ABS(f) >= min()) && ABS(f) <= max());
		// ~8 clock cycles on modern x86-64
		const CastF2I ufi(f);
		int32_t t1 = ufi.i & 0x7fffffff;      // Non-sign bits
		int32_t t2 = ufi.i & 0x80000000;      // Sign bit
		int32_t t3 = ufi.i & 0x7f800000;      // Exponent
		t1 >>= 13;                            // Align mantissa on MSB
		t2 >>= 16;                            // Shift sign bit into position
		t1 -= 0x1c000;                        // Adjust bias
		t1 = (t3 < 0x38800000) ? 0 : t1;      // Flush-to-zero
		t1 = (t3 > 0x47000000) ? 0x7bff : t1; // Clamp-to-max
		t1 = (t3 == 0 ? 0 : t1);              // Denormals-as-zero
		return (short)(t1 | t2);              // Re-insert sign bit
	}
	static inline float toFloat(short h) {
		// ~6 clock cycles on modern x86-64
		CastF2I ufi;
		int32_t t1 = h & 0x7fff; // Non-sign bits
		int32_t t2 = h & 0x8000; // Sign bit
		int32_t t3 = h & 0x7c00; // Exponent
		t1 <<= 13;               // Align mantissa on MSB
		t2 <<= 16;               // Shift sign bit into position
		t1 += 0x38000000;        // Adjust bias
		t1 = (t3 == 0 ? 0 : t1); // Denormals-as-zero
		ufi.i = t1 | t2;         // Re-insert sign bit
		return ufi.f;
	}

	#ifdef _USE_BOOST
	// serialize
	template <class Archive>
	void serialize(Archive& ar, const unsigned int /*version*/) {
		ar & val;
	}
	#endif

protected:
	static inline hfloat fromFloat2HFloat(float f) {
		return TAliasCast<short,hfloat>(fromFloat(f)).i;
	}

protected:
	Type val;
};
/*----------------------------------------------------------------*/

} // namespace SEACAVE

#endif // __SEACAVE_HALFFLOAT_H__
