2024-03-19 17:45:12 +08:00

622 lines
22 KiB
C++

/**************************************************************************************/
/* */
/* Visualization Library */
/* http://visualizationlibrary.org */
/* */
/* Copyright (c) 2005-2020, Michele Bosi */
/* All rights reserved. */
/* */
/* Redistribution and use in source and binary forms, with or without modification, */
/* are permitted provided that the following conditions are met: */
/* */
/* - Redistributions of source code must retain the above copyright notice, this */
/* list of conditions and the following disclaimer. */
/* */
/* - Redistributions in binary form must reproduce the above copyright notice, this */
/* list of conditions and the following disclaimer in the documentation and/or */
/* other materials provided with the distribution. */
/* */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND */
/* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */
/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
/* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR */
/* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
/* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
/* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
/* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
/* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/* */
/**************************************************************************************/
#ifndef HalfFloat_INCLUDE_ONCE
#define HalfFloat_INCLUDE_ONCE
#include <vlCore/Matrix4.hpp>
namespace vl
{
//! Represents an half-precision floating point value.
class half
{
public:
half(): bits(0) {}
half(const half& hf): bits(hf.bits) {}
half(int i): bits(convertFloatToHalf((float)i).bits) {}
half(long long i): bits(convertFloatToHalf((float)i).bits) {}
half(float f): bits(convertFloatToHalf(f).bits) {}
half(double d): bits(convertFloatToHalf((float)d).bits) {}
operator float() const
{
return convertHalfToFloat(*this);
}
operator double() const
{
return (double)convertHalfToFloat(*this);
}
operator int() const
{
return (int)convertHalfToFloat(*this);
}
operator long long() const
{
return (long long)convertHalfToFloat(*this);
}
half& operator=(const half& other)
{
bits = other.bits;
return *this;
}
half operator+(const half& other) const
{
return convertFloatToHalf( convertHalfToFloat(*this) + convertHalfToFloat(other) );
}
half& operator+=(const half& other)
{
return *this = convertFloatToHalf( convertHalfToFloat(*this) + convertHalfToFloat(other) );
}
half operator-(const half& other) const
{
return convertFloatToHalf( convertHalfToFloat(*this) - convertHalfToFloat(other) );
}
half& operator-=(const half& other)
{
return *this = convertFloatToHalf( convertHalfToFloat(*this) - convertHalfToFloat(other) );
}
half operator*(const half& other) const
{
return convertFloatToHalf( convertHalfToFloat(*this) * convertHalfToFloat(other) );
}
half& operator*=(const half& other)
{
return *this = convertFloatToHalf( convertHalfToFloat(*this) * convertHalfToFloat(other) );
}
half operator/(const half& other) const
{
return convertFloatToHalf( convertHalfToFloat(*this) / convertHalfToFloat(other) );
}
half& operator/=(const half& other)
{
return *this = convertFloatToHalf( convertHalfToFloat(*this) / convertHalfToFloat(other) );
}
bool isZero() const
{
return (bits & ((1 << 15)-1)) == 0;
}
operator bool() const
{
return !isZero();
}
bool operator==(const half& other) const
{
if (isNaN() && other.isNaN())
return false;
else
if (isZero() && other.isZero())
return true;
else
return bits == other.bits;
}
bool operator==(const float& other) const
{
return operator==( convertFloatToHalf(other) );
}
bool operator==(const double& other) const
{
return operator==( convertFloatToHalf((float)other) );
}
bool operator==(const int& other) const
{
return operator==( convertFloatToHalf((float)other) );
}
bool operator==(const long long& other) const
{
return operator==( convertFloatToHalf((float)other) );
}
bool operator!=(const half& other) const
{
if (isNaN() && other.isNaN())
return false;
else
if (isZero() && other.isZero())
return false;
else
return bits != other.bits;
}
bool operator!=(const float& other) const
{
return operator!=( convertFloatToHalf(other) );
}
bool operator!=(const double& other) const
{
return operator!=( convertFloatToHalf((float)other) );
}
bool operator!=(const int& other) const
{
return operator!=( convertFloatToHalf((float)other) );
}
bool operator!=(const long long& other) const
{
return operator!=( convertFloatToHalf((float)other) );
}
bool operator<(const half& other) const
{
return convertHalfToFloat(*this) < convertHalfToFloat(other);
}
bool operator>(const half& other) const
{
return convertHalfToFloat(*this) < convertHalfToFloat(other);
}
bool isNaN() const
{
unsigned int mantissa = (unsigned int) (bits & (( 1 << 10) - 1) );
unsigned int exp = (unsigned int) (bits & HALF_FLOAT_MAX_BIASED_EXP);
return exp == HALF_FLOAT_MAX_BIASED_EXP && mantissa != 0;
}
bool isinf() const
{
unsigned int mantissa = (unsigned int) (bits & (( 1 << 10) - 1) );
unsigned int exp = (unsigned int) (bits & HALF_FLOAT_MAX_BIASED_EXP);
return exp == HALF_FLOAT_MAX_BIASED_EXP && mantissa == 0;
}
bool isinf_pos() const
{
unsigned int sign = (unsigned int) ( bits >> 15);
unsigned int mantissa = (unsigned int) (bits & (( 1 << 10) - 1) );
unsigned int exp = (unsigned int) (bits & HALF_FLOAT_MAX_BIASED_EXP);
return exp == HALF_FLOAT_MAX_BIASED_EXP && mantissa == 0 && sign == 0;
}
bool isinf_neg() const
{
unsigned int sign = (unsigned int) ( bits >> 15);
unsigned int mantissa = (unsigned int) (bits & (( 1 << 10) - 1) );
unsigned int exp = (unsigned int) (bits & HALF_FLOAT_MAX_BIASED_EXP);
return exp == HALF_FLOAT_MAX_BIASED_EXP && mantissa == 0 && sign == 1;
}
bool isdenorm() const
{
unsigned int mantissa = (unsigned int) (bits & (( 1 << 10) - 1) );
unsigned int exp = (unsigned int) (bits & HALF_FLOAT_MAX_BIASED_EXP);
return exp == 0 && mantissa != 0;
}
half operator-() const
{
half h = *this;
h.bits ^= 1 << 15;
return h;
}
//---------------------------------------------------------------------------
static half infinity()
{
half h;
h.bits = HALF_FLOAT_MAX_BIASED_EXP;
return h;
}
//---------------------------------------------------------------------------
static half NaN()
{
half h;
h.bits = HALF_FLOAT_MAX_BIASED_EXP | (( 1 << 10) - 1);
return h;
}
//---------------------------------------------------------------------------
static void convertDoubleToHalf(const double* d, half* h, int count)
{
for(int i=0; i<count; ++i)
h[i] = convertFloatToHalf((float)d[i]);
}
//---------------------------------------------------------------------------
static void convertHalfToDouble(const half* h, double* d, int count)
{
for(int i=0; i<count; ++i)
d[i] = (double)convertHalfToFloat(h[i]);
}
//---------------------------------------------------------------------------
static half convertFloatToHalf(float f)
{
union { float f; unsigned int x; } val;
val.f = f;
unsigned int sign = (unsigned short)(val.x>>31);
unsigned int mantissa = val.x & ((1 << 23)-1);
unsigned int exp = val.x & FLOAT_MAX_BIASED_EXP;
typedef unsigned short hfloat;
half hf;
if (exp >= HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP)
{
// check if the original single precision float number is a NaN
if (mantissa && (exp == FLOAT_MAX_BIASED_EXP))
{
// we have a single precision NaN
mantissa = (1<<23) - 1;
}
else
{
// 16-bit half-float representation stores number as Inf
mantissa = 0;
}
hf.bits = (((hfloat)sign) << 15) | (hfloat)(HALF_FLOAT_MAX_BIASED_EXP) | (hfloat)(mantissa >> 13);
}
// check if exponent is <= -15
else
if (exp <= HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP)
{
// store a denorm half-float value or zero
exp = (HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP - exp) >> 23;
mantissa >>= (14 + exp);
hf.bits = (((hfloat)sign) << 15) | (hfloat)(mantissa);
}
else
{
hf.bits = (((hfloat)sign)<<15) |
(hfloat)((exp - HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP) >> 13) |
(hfloat)(mantissa >> 13);
}
return hf;
}
//---------------------------------------------------------------------------
static float convertHalfToFloat(const half& h)
{
unsigned short hf = h.bits;
unsigned int sign = (unsigned int) ( hf >> 15);
unsigned int mantissa = (unsigned int) (hf & (( 1 << 10) - 1) );
unsigned int exp = (unsigned int) (hf & HALF_FLOAT_MAX_BIASED_EXP);
if (exp == HALF_FLOAT_MAX_BIASED_EXP)
{
// we have a half-float NaN or Inf
// half-float NaNs will be converted to a single precision NaN
// half-float Infs will be converted to a single precision Inf
exp = FLOAT_MAX_BIASED_EXP;
if ( mantissa)
mantissa = (1 << 23 ) - 1; // set all bits to indicate a NaN
}
else if (exp == 0x0)
{
// convert half-float zero/denorm to single precision value
if ( mantissa)
{
mantissa <<= 1;
exp = HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
// check for leading 1 in denorm mantissa
while ((mantissa & (1 << 10) ) == 0)
{
// for every leading 0, decrement single precision exponent by 1
// and shift half-float mantissa value to the left
mantissa <<= 1;
exp -= (1 << 23 );
}
// clamp the mantissa to 10-bits
mantissa &= (( 1 << 10) - 1);
// shift left to generate single-precision mantissa of 23-bits
mantissa <<= 13;
}
}
else
{
// shift left to generate single-precision mantissa of 23-bits
mantissa <<= 13;
// generate single precision biased exponent value
exp = ( exp << 13) + HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
}
union { float f; unsigned int x; } val;
val.x = ( sign << 31) | exp | mantissa;
return val.f;
}
//---------------------------------------------------------------------------
static void convertFloatToHalf(const float* f, half* h, int count)
{
for(int i=0; i<count; ++i)
{
union { float f; unsigned int x; } val;
val.f = f[i];
unsigned int sign = (unsigned short)(val.x>>31);
unsigned int mantissa = val.x & ((1 << 23)-1);
unsigned int exp = val.x & FLOAT_MAX_BIASED_EXP;
typedef unsigned short hfloat;
hfloat& hf = h[i].bits;
if (exp >= HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP)
{
// check if the original single precision float number is a NaN
if (mantissa && (exp == FLOAT_MAX_BIASED_EXP))
{
// we have a single precision NaN
mantissa = (1<<23) - 1;
}
else
{
// 16-bit half-float representation stores number as Inf
mantissa = 0;
}
hf = (((hfloat)sign) << 15) | (hfloat)(HALF_FLOAT_MAX_BIASED_EXP) | (hfloat)(mantissa >> 13);
}
// check if exponent is <= -15
else
if (exp <= HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP)
{
// store a denorm half-float value or zero
exp = (HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP - exp) >> 23;
mantissa >>= (14 + exp);
hf = (((hfloat)sign) << 15) | (hfloat)(mantissa);
}
else
{
hf = (((hfloat)sign)<<15) |
(hfloat)((exp - HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP) >> 13) |
(hfloat)(mantissa >> 13);
}
}
}
//---------------------------------------------------------------------------
static void convertHalfToFloat(const half* h, float *f, int count)
{
for(int i=0; i<count; ++i)
{
const unsigned short& hf = h[i].bits;
unsigned int sign = (unsigned int) ( hf >> 15);
unsigned int mantissa = (unsigned int) (hf & (( 1 << 10) - 1) );
unsigned int exp = (unsigned int) (hf & HALF_FLOAT_MAX_BIASED_EXP);
if (exp == HALF_FLOAT_MAX_BIASED_EXP)
{
// we have a half-float NaN or Inf
// half-float NaNs will be converted to a single precision NaN
// half-float Infs will be converted to a single precision Inf
exp = FLOAT_MAX_BIASED_EXP;
if ( mantissa)
mantissa = (1 << 23 ) - 1; // set all bits to indicate a NaN
}
else if (exp == 0x0)
{
// convert half-float zero/denorm to single precision value
if ( mantissa)
{
mantissa <<= 1;
exp = HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
// check for leading 1 in denorm mantissa
while ((mantissa & (1 << 10) ) == 0)
{
// for every leading 0, decrement single precision exponent by 1
// and shift half-float mantissa value to the left
mantissa <<= 1;
exp -= (1 << 23 );
}
// clamp the mantissa to 10-bits
mantissa &= (( 1 << 10) - 1);
// shift left to generate single-precision mantissa of 23-bits
mantissa <<= 13;
}
}
else
{
// shift left to generate single-precision mantissa of 23-bits
mantissa <<= 13;
// generate single precision biased exponent value
exp = ( exp << 13) + HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
}
union { float f; unsigned int x; } val;
val.x = ( sign << 31) | exp | mantissa;
f[i] = val.f;
}
}
//---------------------------------------------------------------------------
public:
unsigned short bits;
private:
// -15 stored using a single precision bias of 127
static const unsigned int HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP = 0x38000000;
// max exponent value in single precision that will be converted
// to Inf or Nan when stored as a half-float
static const unsigned int HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP = 0x47800000;
// 255 is the max exponent biased value
static const unsigned int FLOAT_MAX_BIASED_EXP = (0xFF << 23);
static const unsigned int HALF_FLOAT_MAX_BIASED_EXP = (0x1F << 10);
};
//-----------------------------------------------------------------------------
inline float operator/(float a, const half& b)
{
return (float)a / half::convertHalfToFloat(b);
}
//-----------------------------------------------------------------------------
inline float operator/(double a, const half& b)
{
return (float)a / half::convertHalfToFloat(b);
}
//-----------------------------------------------------------------------------
inline float operator/(int a, const half& b)
{
return (float)a / half::convertHalfToFloat(b);
}
//-----------------------------------------------------------------------------
inline float operator*(float a, const half& b)
{
return (float)a * half::convertHalfToFloat(b);
}
//-----------------------------------------------------------------------------
inline float operator*(double a, const half& b)
{
return (float)a * half::convertHalfToFloat(b);
}
//-----------------------------------------------------------------------------
inline float operator*(int a, const half& b)
{
return (float)a * half::convertHalfToFloat(b);
}
//-----------------------------------------------------------------------------
inline float operator+(float a, const half& b)
{
return (float)a + half::convertHalfToFloat(b);
}
//-----------------------------------------------------------------------------
inline float operator+(double a, const half& b)
{
return (float)a + half::convertHalfToFloat(b);
}
//-----------------------------------------------------------------------------
inline float operator+(int a, const half& b)
{
return (float)a + half::convertHalfToFloat(b);
}
//-----------------------------------------------------------------------------
inline float operator-(float a, const half& b)
{
return (float)a - half::convertHalfToFloat(b);
}
//-----------------------------------------------------------------------------
inline float operator-(double a, const half& b)
{
return (float)a - half::convertHalfToFloat(b);
}
//-----------------------------------------------------------------------------
inline float operator-(int a, const half& b)
{
return (float)a - half::convertHalfToFloat(b);
}
//-----------------------------------------------------------------------------
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//-----------------------------------------------------------------------------
inline float operator/(const half& a, float b)
{
return half::convertHalfToFloat(a) / (float)b ;
}
//-----------------------------------------------------------------------------
inline float operator/(const half& a, double b)
{
return half::convertHalfToFloat(a) / (float)b ;
}
//-----------------------------------------------------------------------------
inline float operator/(const half& a, int b)
{
return half::convertHalfToFloat(a) / (float)b ;
}
//-----------------------------------------------------------------------------
inline float operator*(const half& a, float b)
{
return half::convertHalfToFloat(a) * (float)b ;
}
//-----------------------------------------------------------------------------
inline float operator*(const half& a, double b)
{
return half::convertHalfToFloat(a) * (float)b ;
}
//-----------------------------------------------------------------------------
inline float operator*(const half& a, int b)
{
return half::convertHalfToFloat(a) * (float)b ;
}
//-----------------------------------------------------------------------------
inline float operator+(const half& a, float b)
{
return half::convertHalfToFloat(a) + (float)b ;
}
//-----------------------------------------------------------------------------
inline float operator+(const half& a, double b)
{
return half::convertHalfToFloat(a) + (float)b ;
}
//-----------------------------------------------------------------------------
inline float operator+(const half& a, int b)
{
return half::convertHalfToFloat(a) + (float)b ;
}
//-----------------------------------------------------------------------------
inline float operator-(const half& a, float b)
{
return half::convertHalfToFloat(a) - (float)b ;
}
//-----------------------------------------------------------------------------
inline float operator-(const half& a, double b)
{
return half::convertHalfToFloat(a) - (float)b ;
}
//-----------------------------------------------------------------------------
inline float operator-(const half& a, int b)
{
return half::convertHalfToFloat(a) - (float)b ;
}
//-----------------------------------------------------------------------------
template<> inline half Vector4<half>::length() const { return (half)::sqrt( (float)x()*(float)x()+(float)y()*(float)y()+(float)z()*(float)z()+(float)w()*(float)w()); }
template<> inline half Vector4<half>::lengthSquared() const { return (half)((float)x()*(float)x()+(float)y()*(float)y()+(float)z()*(float)z()+(float)w()*(float)w()); }
template<> inline half Vector3<half>::length() const { return (half)::sqrt( (float)x()*(float)x()+(float)y()*(float)y()+(float)z()*(float)z()); }
template<> inline half Vector3<half>::lengthSquared() const { return (half)((float)x()*(float)x()+(float)y()*(float)y()+(float)z()*(float)z()); }
template<> inline half Vector2<half>::length() const { return (half)::sqrt( (float)x()*(float)x()+(float)y()*(float)y()); }
template<> inline half Vector2<half>::lengthSquared() const { return (half)((float)x()*(float)x()+(float)y()*(float)y()); }
//-----------------------------------------------------------------------------
typedef Vector4<half> hvec4;
typedef Vector3<half> hvec3;
typedef Vector2<half> hvec2;
//-----------------------------------------------------------------------------
typedef Matrix4<half> hmat4;
typedef Matrix3<half> hmat3;
typedef Matrix2<half> hmat2;
//-----------------------------------------------------------------------------
}
#endif