]> git.lizzy.rs Git - minetest.git/blob - src/util/ieee_float.cpp
Simplify bit flip in sha1.cpp
[minetest.git] / src / util / ieee_float.cpp
1 /*
2  * Conversion of f32 to IEEE-754 and vice versa.
3  *
4  * © Copyright 2018 Pedro Gimeno Fortea.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #include "ieee_float.h"
26 #include "log.h"
27 #include "porting.h"
28 #include <limits>
29 #include <cmath>
30
31 // Given an unsigned 32-bit integer representing an IEEE-754 single-precision
32 // float, return the float.
33 f32 u32Tof32Slow(u32 i)
34 {
35         // clang-format off
36         int exp = (i >> 23) & 0xFF;
37         u32 sign = i & 0x80000000UL;
38         u32 imant = i & 0x7FFFFFUL;
39         if (exp == 0xFF) {
40                 // Inf/NaN
41                 if (imant == 0) {
42                         if (std::numeric_limits<f32>::has_infinity)
43                                 return sign ? -std::numeric_limits<f32>::infinity() :
44                                         std::numeric_limits<f32>::infinity();
45                         return sign ? std::numeric_limits<f32>::max() :
46                                 std::numeric_limits<f32>::lowest();
47                 }
48                 return std::numeric_limits<f32>::has_quiet_NaN ?
49                         std::numeric_limits<f32>::quiet_NaN() : -0.f;
50         }
51
52         if (!exp) {
53                 // Denormal or zero
54                 return sign ? -ldexpf((f32)imant, -149) : ldexpf((f32)imant, -149);
55         }
56
57         return sign ? -ldexpf((f32)(imant | 0x800000UL), exp - 150) :
58                 ldexpf((f32)(imant | 0x800000UL), exp - 150);
59         // clang-format on
60 }
61
62 // Given a float, return an unsigned 32-bit integer representing the f32
63 // in IEEE-754 single-precision format.
64 u32 f32Tou32Slow(f32 f)
65 {
66         u32 signbit = std::copysign(1.0f, f) == 1.0f ? 0 : 0x80000000UL;
67         if (f == 0.f)
68                 return signbit;
69         if (std::isnan(f))
70                 return signbit | 0x7FC00000UL;
71         if (std::isinf(f))
72                 return signbit | 0x7F800000UL;
73         int exp = 0; // silence warning
74         f32 mant = frexpf(f, &exp);
75         u32 imant = (u32)std::floor((signbit ? -16777216.f : 16777216.f) * mant);
76         exp += 126;
77         if (exp <= 0) {
78                 // Denormal
79                 return signbit | (exp <= -31 ? 0 : imant >> (1 - exp));
80         }
81
82         if (exp >= 255) {
83                 // Overflow due to the platform having exponents bigger than IEEE ones.
84                 // Return signed infinity.
85                 return signbit | 0x7F800000UL;
86         }
87
88         // Regular number
89         return signbit | (exp << 23) | (imant & 0x7FFFFFUL);
90 }
91
92 // This test needs the following requisites in order to work:
93 // - The float type must be a 32 bits IEEE-754 single-precision float.
94 // - The endianness of f32s and integers must match.
95 FloatType getFloatSerializationType()
96 {
97         // clang-format off
98         const f32 cf = -22220490.f;
99         const u32 cu = 0xCBA98765UL;
100         if (std::numeric_limits<f32>::is_iec559 && sizeof(cf) == 4 &&
101                         sizeof(cu) == 4 && !memcmp(&cf, &cu, 4)) {
102                 // u32Tof32Slow and f32Tou32Slow are not needed, use memcpy
103                 return FLOATTYPE_SYSTEM;
104         }
105
106         // Run quick tests to ensure the custom functions provide acceptable results
107         warningstream << "floatSerialization: f32 and u32 endianness are "
108                 "not equal or machine is not IEEE-754 compliant" << std::endl;
109         u32 i;
110         char buf[128];
111
112         // NaN checks aren't included in the main loop
113         if (!std::isnan(u32Tof32Slow(0x7FC00000UL))) {
114                 porting::mt_snprintf(buf, sizeof(buf),
115                         "u32Tof32Slow(0x7FC00000) failed to produce a NaN, actual: %.9g",
116                         u32Tof32Slow(0x7FC00000UL));
117                 infostream << buf << std::endl;
118         }
119         if (!std::isnan(u32Tof32Slow(0xFFC00000UL))) {
120                 porting::mt_snprintf(buf, sizeof(buf),
121                         "u32Tof32Slow(0xFFC00000) failed to produce a NaN, actual: %.9g",
122                         u32Tof32Slow(0xFFC00000UL));
123                 infostream << buf << std::endl;
124         }
125
126         i = f32Tou32Slow(std::numeric_limits<f32>::quiet_NaN());
127         // check that it corresponds to a NaN encoding
128         if ((i & 0x7F800000UL) != 0x7F800000UL || (i & 0x7FFFFFUL) == 0) {
129                 porting::mt_snprintf(buf, sizeof(buf),
130                         "f32Tou32Slow(NaN) failed to encode NaN, actual: 0x%X", i);
131                 infostream << buf << std::endl;
132         }
133
134         return FLOATTYPE_SLOW;
135         // clang-format on
136 }