2021-11-27 10:28:52 -05:00
|
|
|
//
|
|
|
|
// FILE: float16.cpp
|
|
|
|
// AUTHOR: Rob Tillaart
|
2022-11-07 08:26:49 -05:00
|
|
|
// VERSION: 0.1.7
|
2021-11-27 10:28:52 -05:00
|
|
|
// PURPOSE: library for Float16s for Arduino
|
|
|
|
// URL: http://en.wikipedia.org/wiki/Half-precision_floating-point_format
|
|
|
|
//
|
2022-11-07 08:26:49 -05:00
|
|
|
// HISTORY: see changelog.md
|
2021-11-27 10:28:52 -05:00
|
|
|
|
|
|
|
|
|
|
|
#include "float16.h"
|
|
|
|
|
|
|
|
// #define DEBUG
|
|
|
|
|
2021-12-18 08:15:24 -05:00
|
|
|
|
2021-11-27 10:28:52 -05:00
|
|
|
// CONSTRUCTOR
|
|
|
|
float16::float16(double f)
|
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
_value = f32tof16(f);
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// PRINTING
|
|
|
|
size_t float16::printTo(Print& p) const
|
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
double d = this->f16tof32(_value);
|
|
|
|
return p.print(d, _decimals);
|
2021-11-27 10:28:52 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
double float16::toDouble() const
|
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
return f16tof32(_value);
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
//////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// EQUALITIES
|
|
|
|
//
|
|
|
|
bool float16::operator == (const float16 &f)
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
return (_value == f._value);
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
bool float16::operator != (const float16 &f)
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
return (_value != f._value);
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
bool float16::operator > (const float16 &f)
|
|
|
|
{
|
|
|
|
if ((_value & 0x8000) && ( f._value & 0x8000)) return _value < f._value;
|
|
|
|
if (_value & 0x8000) return false;
|
|
|
|
if (f._value & 0x8000) return true;
|
|
|
|
return _value > f._value;
|
|
|
|
}
|
2021-11-27 10:28:52 -05:00
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
bool float16::operator >= (const float16 &f)
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
if ((_value & 0x8000) && (f._value & 0x8000)) return _value <= f._value;
|
|
|
|
if (_value & 0x8000) return false;
|
|
|
|
if (f._value & 0x8000) return true;
|
|
|
|
return _value >= f._value;
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
bool float16::operator < (const float16 &f)
|
|
|
|
{
|
|
|
|
if ((_value & 0x8000) && (f._value & 0x8000)) return _value > f._value;
|
|
|
|
if (_value & 0x8000) return true;
|
|
|
|
if (f._value & 0x8000) return false;
|
|
|
|
return _value < f._value;
|
|
|
|
}
|
2021-11-27 10:28:52 -05:00
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
bool float16::operator <= (const float16 &f)
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
if ((_value & 0x8000) && (f._value & 0x8000)) return _value >= f._value;
|
|
|
|
if (_value & 0x8000) return true;
|
|
|
|
if (f._value & 0x8000) return false;
|
|
|
|
return _value <= f._value;
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
//////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// NEGATION
|
|
|
|
//
|
|
|
|
float16 float16::operator - ()
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
float16 f16;
|
|
|
|
f16.setBinary(_value ^ 0x8000);
|
|
|
|
return f16;
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
//////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// MATH
|
|
|
|
//
|
|
|
|
float16 float16::operator + (const float16 &f)
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
return float16(this->toDouble() + f.toDouble());
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
float16 float16::operator - (const float16 &f)
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
return float16(this->toDouble() - f.toDouble());
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
float16 float16::operator * (const float16 &f)
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
return float16(this->toDouble() * f.toDouble());
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
float16 float16::operator / (const float16 &f)
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
return float16(this->toDouble() / f.toDouble());
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
float16& float16::operator += (const float16 &f)
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
*this = this->toDouble() + f.toDouble();
|
|
|
|
return *this;
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
float16& float16::operator -= (const float16 &f)
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
*this = this->toDouble() - f.toDouble();
|
|
|
|
return *this;
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
float16& float16::operator *= (const float16 &f)
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
*this = this->toDouble() * f.toDouble();
|
|
|
|
return *this;
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
float16& float16::operator /= (const float16 &f)
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
*this = this->toDouble() / f.toDouble();
|
|
|
|
return *this;
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// MATH HELPER FUNCTIONS
|
|
|
|
//
|
|
|
|
|
|
|
|
int float16::sign()
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
if (_value & 0x8000) return -1;
|
|
|
|
if (_value & 0xFFFF) return 1;
|
|
|
|
return 0;
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
bool float16::isZero()
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
2021-12-02 14:27:32 -05:00
|
|
|
return ((_value & 0x7FFF) == 0x0000);
|
2021-11-27 10:28:52 -05:00
|
|
|
}
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
// bool float16::isNaN()
|
|
|
|
// {
|
|
|
|
// return ((_value & 0x7FFF) == 0x0000);
|
|
|
|
// }
|
|
|
|
|
|
|
|
bool float16::isInf()
|
|
|
|
{
|
|
|
|
return ((_value == 0x7C00) || (_value == 0xFC00));
|
|
|
|
}
|
2021-11-27 10:28:52 -05:00
|
|
|
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
//////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// CORE CONVERSION
|
|
|
|
//
|
|
|
|
float float16::f16tof32(uint16_t _value) const
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
|
|
|
uint16_t sgn, man;
|
|
|
|
int exp;
|
|
|
|
double f;
|
|
|
|
|
2021-12-02 14:27:32 -05:00
|
|
|
sgn = (_value & 0x8000) > 0;
|
|
|
|
exp = (_value & 0x7C00) >> 10;
|
|
|
|
man = (_value & 0x03FF);
|
2021-11-27 10:28:52 -05:00
|
|
|
|
|
|
|
// ZERO
|
2021-12-02 14:27:32 -05:00
|
|
|
if ((_value & 0x7FFF) == 0)
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
|
|
|
return sgn ? -0 : 0;
|
|
|
|
}
|
|
|
|
// NAN & INF
|
|
|
|
if (exp == 0x001F)
|
|
|
|
{
|
|
|
|
if (man == 0) return sgn ? -INFINITY : INFINITY;
|
|
|
|
else return NAN;
|
|
|
|
}
|
|
|
|
|
|
|
|
// SUBNORMAL/NORMAL
|
|
|
|
if (exp == 0) f = 0;
|
|
|
|
else f = 1;
|
|
|
|
|
|
|
|
// PROCESS MANTISSE
|
|
|
|
for (int i = 9; i >= 0; i--)
|
|
|
|
{
|
|
|
|
f *= 2;
|
|
|
|
if (man & (1 << i)) f = f + 1;
|
|
|
|
}
|
|
|
|
f = f * pow(2.0, exp - 25);
|
|
|
|
if (exp == 0)
|
|
|
|
{
|
|
|
|
f = f * pow(2.0, -13); // 5.96046447754e-8;
|
|
|
|
}
|
|
|
|
return sgn ? -f : f;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t float16::f32tof16(float f) const
|
|
|
|
{
|
|
|
|
uint32_t t = *(uint32_t *) &f;
|
|
|
|
// man bits = 10; but we keep 11 for rounding
|
|
|
|
uint16_t man = (t & 0x007FFFFF) >> 12;
|
|
|
|
int16_t exp = (t & 0x7F800000) >> 23;
|
|
|
|
bool sgn = (t & 0x80000000);
|
|
|
|
|
|
|
|
// handle 0
|
|
|
|
if ((t & 0x7FFFFFFF) == 0)
|
|
|
|
{
|
|
|
|
return sgn ? 0x8000 : 0x0000;
|
|
|
|
}
|
|
|
|
// denormalized float32 does not fit in float16
|
|
|
|
if (exp == 0x00)
|
|
|
|
{
|
|
|
|
return sgn ? 0x8000 : 0x0000;
|
|
|
|
}
|
|
|
|
// handle infinity & NAN
|
|
|
|
if (exp == 0x00FF)
|
|
|
|
{
|
|
|
|
if (man) return 0xFE00; // NAN
|
|
|
|
return sgn ? 0xFC00 : 0x7C00; // -INF : INF
|
|
|
|
}
|
|
|
|
|
|
|
|
// normal numbers
|
|
|
|
exp = exp - 127 + 15;
|
|
|
|
// overflow does not fit => INF
|
2021-12-18 08:15:24 -05:00
|
|
|
if (exp > 30)
|
2021-11-27 10:28:52 -05:00
|
|
|
{
|
|
|
|
return sgn ? 0xFC00 : 0x7C00; // -INF : INF
|
|
|
|
}
|
|
|
|
// subnormal numbers
|
|
|
|
if (exp < -38)
|
|
|
|
{
|
|
|
|
return sgn ? 0x8000 : 0x0000; // -0 or 0 ? just 0 ?
|
|
|
|
}
|
|
|
|
if (exp <= 0) // subnormal
|
|
|
|
{
|
|
|
|
man >>= (exp + 14);
|
|
|
|
// rounding
|
|
|
|
man++;
|
|
|
|
man >>= 1;
|
|
|
|
if (sgn) return 0x8000 | man;
|
|
|
|
return man;
|
|
|
|
}
|
|
|
|
|
|
|
|
// normal
|
|
|
|
// TODO rounding
|
|
|
|
exp <<= 10;
|
|
|
|
man++;
|
|
|
|
man >>= 1;
|
|
|
|
if (sgn) return 0x8000 | exp | man;
|
|
|
|
return exp | man;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// -- END OF FILE --
|
|
|
|
|