diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 957cb70d..dd549268 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -243,6 +243,8 @@ set(BASE_SOURCES math/const.h math/func.h math/geometry.h + math/half.cpp + math/half.h math/intpoint.h math/matrix.h math/point.h diff --git a/src/math/all.h b/src/math/all.h index 7137c61e..f56a56f5 100644 --- a/src/math/all.h +++ b/src/math/all.h @@ -28,6 +28,7 @@ #include "math/const.h" #include "math/func.h" #include "math/geometry.h" +#include "math/half.h" #include "math/matrix.h" #include "math/point.h" #include "math/vector.h" diff --git a/src/math/half.cpp b/src/math/half.cpp new file mode 100644 index 00000000..263a8e79 --- /dev/null +++ b/src/math/half.cpp @@ -0,0 +1,116 @@ +/* +* This file is part of the Colobot: Gold Edition source code +* Copyright (C) 2001-2016, 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 +#include +#include + +// Math module namespace +namespace Math +{ + +//! Converts float to half-float +uint16_t FloatToHalf(float value) +{ + uint16_t sign = (copysign(1.0f, value) > 0.0f ? 0x0000 : 0x8000); + + // Infinity + if (isinf(value)) + { + return sign | 0x7C00; + } + // NaN + else if (isnan(value)) + { + return sign | 0x7FFF; + } + + int exponent; + + float significand = fabs(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(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 = ldexp(static_cast(mantissa), -24); + } + // Infinity + else if ((exponent == 31) && (mantissa == 0)) + { + result = std::numeric_limits::infinity(); + } + // NaN + else if ((exponent == 31) && (mantissa != 0)) + { + result = nanf(""); + } + // Normal number + else + { + result = ldexp(static_cast(mantissa | 0x0400), exponent - 25); + } + + float sign = ((value & 0x8000) == 0 ? 1.0f : -1.0f); + + return copysignf(result, sign); +} + +} // namespace Math diff --git a/src/math/half.h b/src/math/half.h new file mode 100644 index 00000000..a5e44f46 --- /dev/null +++ b/src/math/half.h @@ -0,0 +1,103 @@ +/* +* This file is part of the Colobot: Gold Edition source code +* Copyright (C) 2001-2016, 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 +*/ + +/** +* \file math/half.h +* \brief Implementation of half-precision floating point values. +*/ + +#pragma once + +#include + +// Math module namespace +namespace Math +{ + +//! Converts float to half-float binary representation +uint16_t FloatToHalf(float value); + +//! Converts half-float binary representation to float +float HaltToFloat(uint16_t value); + +/** +* \struct half +* \brief half-precision floating point type +* +* Represents a half-precision floating point value. +* Contains the required methods for converting to and from ints and floats. +* +* This type is for storage only. +* Conversion is expensive and should be avoided if possible. +*/ +struct half +{ + //! 16-bit binary representation of half-float + uint16_t bits; + + //! Default constructor + half() + : bits(0) + { + + } + + //! Copy constructor + half(const half& other) + : bits(other.bits) + { + + } + + //! Copy operator + half& operator=(const half& other) + { + bits = other.bits; + + return *this; + } + + //! Conversion constructor from int + explicit half(int value) + : bits(FloatToHalf(static_cast(value))) + { + + } + + //! Conversion constructor from float + explicit half(float value) + : bits(FloatToHalf(value)) + { + + } + + //! Conversion operator to int + explicit operator int() const + { + return static_cast(HaltToFloat(bits)); + } + + //! Conversion operator to float + explicit operator float() const + { + return HaltToFloat(bits); + } +}; + +} // namespace Math