aboutsummaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authorSmallJoker <SmallJoker@users.noreply.github.com>2018-12-13 11:20:57 +0100
committerLoïc Blot <nerzhul@users.noreply.github.com>2018-12-13 11:20:57 +0100
commit839e935ba0572c592a791cc4dd4df4a9f6d2d260 (patch)
tree6a7d59e1804879dc24e9e4b1e9ce0294d681bedd /src/util
parent8471d027b9896af74d2c1ab3b0068af4953e5ca0 (diff)
downloadminetest-839e935ba0572c592a791cc4dd4df4a9f6d2d260.tar.gz
minetest-839e935ba0572c592a791cc4dd4df4a9f6d2d260.tar.bz2
minetest-839e935ba0572c592a791cc4dd4df4a9f6d2d260.zip
Network: Send IEEE floats (#7768)
Diffstat (limited to 'src/util')
-rw-r--r--src/util/CMakeLists.txt1
-rw-r--r--src/util/ieee_float.cpp136
-rw-r--r--src/util/ieee_float.h34
-rw-r--r--src/util/serialize.cpp2
-rw-r--r--src/util/serialize.h61
5 files changed, 233 insertions, 1 deletions
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt
index f571ab22c..bf208693b 100644
--- a/src/util/CMakeLists.txt
+++ b/src/util/CMakeLists.txt
@@ -4,6 +4,7 @@ set(UTIL_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp
${CMAKE_CURRENT_SOURCE_DIR}/enriched_string.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/ieee_float.cpp
${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp
${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp
${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp
diff --git a/src/util/ieee_float.cpp b/src/util/ieee_float.cpp
new file mode 100644
index 000000000..e7909360f
--- /dev/null
+++ b/src/util/ieee_float.cpp
@@ -0,0 +1,136 @@
+/*
+ * Conversion of f32 to IEEE-754 and vice versa.
+ *
+ * © Copyright 2018 Pedro Gimeno Fortea.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ieee_float.h"
+#include "log.h"
+#include "porting.h"
+#include <limits>
+#include <cmath>
+
+// Given an unsigned 32-bit integer representing an IEEE-754 single-precision
+// float, return the float.
+f32 u32Tof32Slow(u32 i)
+{
+ // clang-format off
+ int exp = (i >> 23) & 0xFF;
+ u32 sign = i & 0x80000000UL;
+ u32 imant = i & 0x7FFFFFUL;
+ if (exp == 0xFF) {
+ // Inf/NaN
+ if (imant == 0) {
+ if (std::numeric_limits<f32>::has_infinity)
+ return sign ? -std::numeric_limits<f32>::infinity() :
+ std::numeric_limits<f32>::infinity();
+ return sign ? std::numeric_limits<f32>::max() :
+ std::numeric_limits<f32>::lowest();
+ }
+ return std::numeric_limits<f32>::has_quiet_NaN ?
+ std::numeric_limits<f32>::quiet_NaN() : -0.f;
+ }
+
+ if (!exp) {
+ // Denormal or zero
+ return sign ? -ldexpf((f32)imant, -149) : ldexpf((f32)imant, -149);
+ }
+
+ return sign ? -ldexpf((f32)(imant | 0x800000UL), exp - 150) :
+ ldexpf((f32)(imant | 0x800000UL), exp - 150);
+ // clang-format on
+}
+
+// Given a float, return an unsigned 32-bit integer representing the f32
+// in IEEE-754 single-precision format.
+u32 f32Tou32Slow(f32 f)
+{
+ u32 signbit = std::copysign(1.0f, f) == 1.0f ? 0 : 0x80000000UL;
+ if (f == 0.f)
+ return signbit;
+ if (std::isnan(f))
+ return signbit | 0x7FC00000UL;
+ if (std::isinf(f))
+ return signbit | 0x7F800000UL;
+ int exp;
+ f32 mant = frexpf(f, &exp);
+ u32 imant = (u32)std::floor((signbit ? -16777216.f : 16777216.f) * mant);
+ exp += 126;
+ if (exp <= 0) {
+ // Denormal
+ return signbit | (exp <= -31 ? 0 : imant >> (1 - exp));
+ }
+
+ if (exp >= 255) {
+ // Overflow due to the platform having exponents bigger than IEEE ones.
+ // Return signed infinity.
+ return signbit | 0x7F800000UL;
+ }
+
+ // Regular number
+ return signbit | (exp << 23) | (imant & 0x7FFFFFUL);
+}
+
+// This test needs the following requisites in order to work:
+// - The float type must be a 32 bits IEEE-754 single-precision float.
+// - The endianness of f32s and integers must match.
+FloatType getFloatSerializationType()
+{
+ // clang-format off
+ const f32 cf = -22220490.f;
+ const u32 cu = 0xCBA98765UL;
+ if (std::numeric_limits<f32>::is_iec559 && sizeof(cf) == 4 &&
+ sizeof(cu) == 4 && !memcmp(&cf, &cu, 4)) {
+ // u32Tof32Slow and f32Tou32Slow are not needed, use memcpy
+ return FLOATTYPE_SYSTEM;
+ }
+
+ // Run quick tests to ensure the custom functions provide acceptable results
+ warningstream << "floatSerialization: f32 and u32 endianness are "
+ "not equal or machine is not IEEE-754 compliant" << std::endl;
+ u32 i;
+ char buf[128];
+
+ // NaN checks aren't included in the main loop
+ if (!std::isnan(u32Tof32Slow(0x7FC00000UL))) {
+ porting::mt_snprintf(buf, sizeof(buf),
+ "u32Tof32Slow(0x7FC00000) failed to produce a NaN, actual: %.9g",
+ u32Tof32Slow(0x7FC00000UL));
+ infostream << buf << std::endl;
+ }
+ if (!std::isnan(u32Tof32Slow(0xFFC00000UL))) {
+ porting::mt_snprintf(buf, sizeof(buf),
+ "u32Tof32Slow(0xFFC00000) failed to produce a NaN, actual: %.9g",
+ u32Tof32Slow(0xFFC00000UL));
+ infostream << buf << std::endl;
+ }
+
+ i = f32Tou32Slow(std::numeric_limits<f32>::quiet_NaN());
+ // check that it corresponds to a NaN encoding
+ if ((i & 0x7F800000UL) != 0x7F800000UL || (i & 0x7FFFFFUL) == 0) {
+ porting::mt_snprintf(buf, sizeof(buf),
+ "f32Tou32Slow(NaN) failed to encode NaN, actual: 0x%X", i);
+ infostream << buf << std::endl;
+ }
+
+ return FLOATTYPE_SLOW;
+ // clang-format on
+}
diff --git a/src/util/ieee_float.h b/src/util/ieee_float.h
new file mode 100644
index 000000000..42b8641d0
--- /dev/null
+++ b/src/util/ieee_float.h
@@ -0,0 +1,34 @@
+/*
+Minetest
+Copyright (C) 2018 SmallJoker <mk939@ymail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#pragma once
+
+#include "irrlichttypes.h"
+
+enum FloatType
+{
+ FLOATTYPE_UNKNOWN,
+ FLOATTYPE_SLOW,
+ FLOATTYPE_SYSTEM
+};
+
+f32 u32Tof32Slow(u32 i);
+u32 f32Tou32Slow(f32 f);
+
+FloatType getFloatSerializationType();
diff --git a/src/util/serialize.cpp b/src/util/serialize.cpp
index 53c7c2add..f0e177d57 100644
--- a/src/util/serialize.cpp
+++ b/src/util/serialize.cpp
@@ -28,6 +28,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iomanip>
#include <vector>
+FloatType g_serialize_f32_type = FLOATTYPE_UNKNOWN;
+
////
//// BufReader
////
diff --git a/src/util/serialize.h b/src/util/serialize.h
index 89bc0b097..016491a2c 100644
--- a/src/util/serialize.h
+++ b/src/util/serialize.h
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_bloated.h"
#include "exceptions.h" // for SerializationError
#include "debug.h" // for assert
+#include "ieee_float.h"
#include "config.h"
#if HAVE_ENDIAN_H
@@ -60,6 +61,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define LONG_STRING_MAX_LEN (64 * 1024 * 1024)
+extern FloatType g_serialize_f32_type;
+
#if HAVE_ENDIAN_H
// use machine native byte swapping routines
// Note: memcpy below is optimized out by modern compilers
@@ -188,6 +191,25 @@ inline f32 readF1000(const u8 *data)
return (f32)readS32(data) / FIXEDPOINT_FACTOR;
}
+inline f32 readF32(const u8 *data)
+{
+ u32 u = readU32(data);
+
+ switch (g_serialize_f32_type) {
+ case FLOATTYPE_SYSTEM: {
+ f32 f;
+ memcpy(&f, &u, 4);
+ return f;
+ }
+ case FLOATTYPE_SLOW:
+ return u32Tof32Slow(u);
+ case FLOATTYPE_UNKNOWN: // First initialization
+ g_serialize_f32_type = getFloatSerializationType();
+ return readF32(data);
+ }
+ throw SerializationError("readF32: Unreachable code");
+}
+
inline video::SColor readARGB8(const u8 *data)
{
video::SColor p(readU32(data));
@@ -245,6 +267,15 @@ inline v3f readV3F1000(const u8 *data)
return p;
}
+inline v3f readV3F32(const u8 *data)
+{
+ v3f p;
+ p.X = (float)readF32(&data[0]);
+ p.Y = (float)readF32(&data[4]);
+ p.Z = (float)readF32(&data[8]);
+ return p;
+}
+
/////////////// write routines ////////////////
inline void writeU8(u8 *data, u8 i)
@@ -259,7 +290,7 @@ inline void writeS8(u8 *data, s8 i)
inline void writeS16(u8 *data, s16 i)
{
- writeU16(data, (u16)i);
+ writeU16(data, (u16)i);
}
inline void writeS32(u8 *data, s32 i)
@@ -278,6 +309,23 @@ inline void writeF1000(u8 *data, f32 i)
writeS32(data, i * FIXEDPOINT_FACTOR);
}
+inline void writeF32(u8 *data, f32 i)
+{
+ switch (g_serialize_f32_type) {
+ case FLOATTYPE_SYSTEM: {
+ u32 u;
+ memcpy(&u, &i, 4);
+ return writeU32(data, u);
+ }
+ case FLOATTYPE_SLOW:
+ return writeU32(data, f32Tou32Slow(i));
+ case FLOATTYPE_UNKNOWN: // First initialization
+ g_serialize_f32_type = getFloatSerializationType();
+ return writeF32(data, i);
+ }
+ throw SerializationError("writeF32: Unreachable code");
+}
+
inline void writeARGB8(u8 *data, video::SColor p)
{
writeU32(data, p.color);
@@ -322,6 +370,13 @@ inline void writeV3F1000(u8 *data, v3f p)
writeF1000(&data[8], p.Z);
}
+inline void writeV3F32(u8 *data, v3f p)
+{
+ writeF32(&data[0], p.X);
+ writeF32(&data[4], p.Y);
+ writeF32(&data[8], p.Z);
+}
+
////
//// Iostream wrapper for data read/write
////
@@ -351,12 +406,14 @@ MAKE_STREAM_READ_FXN(s16, S16, 2);
MAKE_STREAM_READ_FXN(s32, S32, 4);
MAKE_STREAM_READ_FXN(s64, S64, 8);
MAKE_STREAM_READ_FXN(f32, F1000, 4);
+MAKE_STREAM_READ_FXN(f32, F32, 4);
MAKE_STREAM_READ_FXN(v2s16, V2S16, 4);
MAKE_STREAM_READ_FXN(v3s16, V3S16, 6);
MAKE_STREAM_READ_FXN(v2s32, V2S32, 8);
MAKE_STREAM_READ_FXN(v3s32, V3S32, 12);
MAKE_STREAM_READ_FXN(v2f, V2F1000, 8);
MAKE_STREAM_READ_FXN(v3f, V3F1000, 12);
+MAKE_STREAM_READ_FXN(v3f, V3F32, 12);
MAKE_STREAM_READ_FXN(video::SColor, ARGB8, 4);
MAKE_STREAM_WRITE_FXN(u8, U8, 1);
@@ -368,12 +425,14 @@ MAKE_STREAM_WRITE_FXN(s16, S16, 2);
MAKE_STREAM_WRITE_FXN(s32, S32, 4);
MAKE_STREAM_WRITE_FXN(s64, S64, 8);
MAKE_STREAM_WRITE_FXN(f32, F1000, 4);
+MAKE_STREAM_WRITE_FXN(f32, F32, 4);
MAKE_STREAM_WRITE_FXN(v2s16, V2S16, 4);
MAKE_STREAM_WRITE_FXN(v3s16, V3S16, 6);
MAKE_STREAM_WRITE_FXN(v2s32, V2S32, 8);
MAKE_STREAM_WRITE_FXN(v3s32, V3S32, 12);
MAKE_STREAM_WRITE_FXN(v2f, V2F1000, 8);
MAKE_STREAM_WRITE_FXN(v3f, V3F1000, 12);
+MAKE_STREAM_WRITE_FXN(v3f, V3F32, 12);
MAKE_STREAM_WRITE_FXN(video::SColor, ARGB8, 4);
////