colobot/colobot-base/math/half.cpp

117 lines
2.7 KiB
C++

/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2023, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "math/half.h"
#include <cmath>
#include <iomanip>
#include <limits>
// Math module namespace
namespace Math
{
//! Converts float to half-float
uint16_t FloatToHalf(float value)
{
uint16_t sign = (std::copysign(1.0f, value) > 0.0f ? 0x0000 : 0x8000);
// Infinity
if (std::isinf(value))
{
return sign | 0x7C00;
}
// NaN
else if (std::isnan(value))
{
return sign | 0x7FFF;
}
int exponent;
float significand = std::fabs(std::frexp(value, &exponent));
// Exponent bias
exponent += 15;
// Crosses upper boundary, clamp to infinity
if (exponent > 31)
{
return sign | 0x7C00;
}
// Crosses lower boundary, clamp to zero
else if (exponent <= 0)
{
return sign | 0x0000;
}
// Zero
else if (significand < 0.25f)
{
return sign | 0x0000;
}
// Normal value
uint16_t mantissa = static_cast<uint16_t>(std::ldexp(2 * significand - 1, 10));
uint16_t bits = sign | mantissa | ((exponent - 1) << 10);
return bits;
}
//! Converts half-float to float
float HaltToFloat(uint16_t value)
{
int exponent = (value >> 10) & 0x001F;
int mantissa = (value >> 0) & 0x03FF;
float result;
// Zero
if ((exponent == 0) && (mantissa == 0))
{
result = 0.0f;
}
// Subnormal
else if ((exponent == 0) && (mantissa != 0))
{
result = std::ldexp(static_cast<float>(mantissa), -24);
}
// Infinity
else if ((exponent == 31) && (mantissa == 0))
{
result = std::numeric_limits<float>::infinity();
}
// NaN
else if ((exponent == 31) && (mantissa != 0))
{
result = nanf("");
}
// Normal number
else
{
result = std::ldexp(static_cast<float>(mantissa | 0x0400), exponent - 25);
}
float sign = ((value & 0x8000) == 0 ? 1.0f : -1.0f);
return std::copysignf(result, sign);
}
} // namespace Math