Coverage Report

Created: 2026-10-28 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/AK/FloatingPoint.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include <AK/BitCast.h>
10
#include <AK/StdLibExtras.h>
11
#include <AK/Types.h>
12
13
namespace AK {
14
15
template<typename T>
16
struct FloatExtractor;
17
18
#ifdef AK_HAS_FLOAT_128
19
template<>
20
struct FloatExtractor<f128> {
21
    static constexpr FloatExtractor<f128> from_float(f128 f) { return bit_cast<FloatExtractor<f128>>(f); }
22
    constexpr f128 to_float() const { return bit_cast<f128>(*this); }
23
24
    using ComponentType = unsigned __int128;
25
    static constexpr int mantissa_bits = 112;
26
    static constexpr ComponentType mantissa_max = (((ComponentType)1) << 112) - 1;
27
    static constexpr int exponent_bias = 16383;
28
    static constexpr int exponent_bits = 15;
29
    static constexpr unsigned exponent_max = 32767;
30
31
    ComponentType mantissa : 112;
32
    ComponentType exponent : 15;
33
    ComponentType sign : 1;
34
};
35
// Validate that f128 and the FloatExtractor struct are 128 bits.
36
static_assert(AssertSize<f128, 16>());
37
static_assert(AssertSize<FloatExtractor<f128>, sizeof(f128)>());
38
#endif
39
40
#ifdef AK_HAS_FLOAT_80
41
template<>
42
struct FloatExtractor<f80> {
43
0
    static constexpr FloatExtractor<f80> from_float(f80 f) { return bit_cast<FloatExtractor<f80>>(f); }
44
0
    constexpr f80 to_float() const { return bit_cast<f80>(*this); }
45
46
    using ComponentType = unsigned long long;
47
    static constexpr int mantissa_bits = 64;
48
    static constexpr ComponentType mantissa_max = ~0ull;
49
    static constexpr int exponent_bias = 16383;
50
    static constexpr int exponent_bits = 15;
51
    static constexpr unsigned exponent_max = 32767;
52
53
    // This is technically wrong: Extended floating point values really only have 63 bits of mantissa
54
    // and an "integer bit" that behaves in various strange, unintuitive and non-IEEE-754 ways.
55
    // However, since all bit-fiddling float code assumes IEEE floats, it cannot handle this properly.
56
    // If we pretend that 80-bit floats are IEEE floats with 64-bit mantissas, almost everything works correctly
57
    // and we just need a few special cases.
58
    ComponentType mantissa : 64;
59
    ComponentType exponent : 15;
60
    ComponentType sign : 1;
61
};
62
static_assert(AssertSize<FloatExtractor<f80>, sizeof(f80)>());
63
#endif
64
65
template<>
66
struct FloatExtractor<f64> {
67
0
    static constexpr FloatExtractor<f64> from_float(f64 f) { return bit_cast<FloatExtractor<f64>>(f); }
68
0
    constexpr f64 to_float() const { return bit_cast<f64>(*this); }
69
70
    using ComponentType = unsigned long long;
71
    static constexpr int mantissa_bits = 52;
72
    static constexpr ComponentType mantissa_max = (1ull << 52) - 1;
73
    static constexpr int exponent_bias = 1023;
74
    static constexpr int exponent_bits = 11;
75
    static constexpr unsigned exponent_max = 2047;
76
77
    // FIXME: These types have to all be the same, otherwise this struct
78
    //        goes from being a bitfield describing the layout of an f64
79
    //        into being a multibyte mess on windows.
80
    //        Technically, '-mno-ms-bitfields' is supposed to disable this
81
    //        very intuitive and portable behaviour on windows, but it doesn't
82
    //        work with the msvc ABI.
83
    //        See <https://github.com/llvm/llvm-project/issues/24757>
84
    ComponentType mantissa : 52;
85
    ComponentType exponent : 11;
86
    ComponentType sign : 1;
87
};
88
static_assert(AssertSize<FloatExtractor<f64>, sizeof(f64)>());
89
90
#ifdef AK_LONG_DOUBLE_IS_DOUBLE
91
template<>
92
struct FloatExtractor<long double> : FloatExtractor<double> {
93
};
94
static_assert(AssertSize<FloatExtractor<long double>, sizeof(FloatExtractor<double>)>());
95
#endif
96
97
template<>
98
struct FloatExtractor<f32> {
99
4.00M
    static constexpr FloatExtractor<f32> from_float(f32 f) { return bit_cast<FloatExtractor<f32>>(f); }
100
462k
    constexpr f32 to_float() const { return bit_cast<f32>(*this); }
101
102
    using ComponentType = unsigned;
103
    static constexpr int mantissa_bits = 23;
104
    static constexpr ComponentType mantissa_max = (1 << 23) - 1;
105
    static constexpr int exponent_bias = 127;
106
    static constexpr int exponent_bits = 8;
107
    static constexpr ComponentType exponent_max = 255;
108
109
    ComponentType mantissa : 23;
110
    ComponentType exponent : 8;
111
    ComponentType sign : 1;
112
};
113
static_assert(AssertSize<FloatExtractor<f32>, sizeof(f32)>());
114
115
template<size_t S, size_t E, size_t M>
116
requires(S <= 1 && E >= 1 && M >= 1 && (S + E + M) <= 64) class FloatingPointBits final {
117
public:
118
    static size_t const signbit = S;
119
    static size_t const exponentbits = E;
120
    static size_t const mantissabits = M;
121
122
    template<typename T>
123
    requires(IsIntegral<T> && IsUnsigned<T> && sizeof(T) <= 8) constexpr FloatingPointBits(T bits)
124
        : m_bits(bits)
125
    {
126
    }
127
128
    constexpr FloatingPointBits(double value)
129
        : m_bits(bit_cast<u64>(value))
130
    {
131
    }
132
133
    constexpr FloatingPointBits(float value)
134
        : m_bits(bit_cast<u32>(value))
135
    {
136
    }
137
138
    double as_double() const
139
    requires(S == 1 && E == 11 && M == 52)
140
    {
141
        return bit_cast<double>(m_bits);
142
    }
143
    float as_float() const
144
    requires(S == 1 && E == 8 && M == 23)
145
    {
146
        return bit_cast<float>(static_cast<u32>(m_bits));
147
    }
148
    u64 bits() const { return m_bits; }
149
150
private:
151
    u64 m_bits;
152
};
153
154
typedef FloatingPointBits<1, 8, 23> SingleFloatingPointBits;
155
typedef FloatingPointBits<1, 11, 52> DoubleFloatingPointBits;
156
157
/**
158
 * Convert between two IEEE 754 floating point types in any arrangement of sign, exponent and mantissa bits.
159
 */
160
template<typename To, typename From>
161
constexpr To float_to_float(From const input)
162
{
163
    constexpr u64 from_exponent_nonnumber = (1ull << From::exponentbits) - 1;
164
    constexpr u64 from_exponent_bias = (1ull << (From::exponentbits - 1)) - 1;
165
    constexpr u64 to_exponent_nonnumber = (1ull << To::exponentbits) - 1;
166
    constexpr u64 to_exponent_bias = (1ull << (To::exponentbits - 1)) - 1;
167
    constexpr u64 to_exponent_max = (1ull << To::exponentbits) - 2;
168
169
    // Deconstruct input bits to float components
170
    u64 from_sign = (input.bits() >> (From::exponentbits + From::mantissabits)) & From::signbit;
171
    u64 from_exponent = (input.bits() >> From::mantissabits) & ((1ull << From::exponentbits) - 1);
172
    u64 from_mantissa = input.bits() & ((1ull << From::mantissabits) - 1);
173
174
    u64 to_sign = from_sign & To::signbit;
175
    u64 to_exponent;
176
    u64 to_mantissa;
177
    auto target_value = [&to_sign, &to_exponent, &to_mantissa]() {
178
        return To((to_sign << (To::exponentbits + To::mantissabits)) | (to_exponent << To::mantissabits) | to_mantissa);
179
    };
180
181
    auto shift_mantissa = [](u64 mantissa) -> u64 {
182
        if constexpr (From::mantissabits < To::mantissabits)
183
            return mantissa << (To::mantissabits - From::mantissabits);
184
        else
185
            return mantissa >> (From::mantissabits - To::mantissabits);
186
    };
187
188
    // If target is unsigned and source is negative, clamp to 0 or keep NaN
189
    if constexpr (To::signbit == 0) {
190
        if (from_sign == 1) {
191
            if (from_exponent == from_exponent_nonnumber && from_mantissa > 0) {
192
                to_exponent = to_exponent_nonnumber;
193
                to_mantissa = 1;
194
            } else {
195
                to_exponent = 0;
196
                to_mantissa = 0;
197
            }
198
            return target_value();
199
        }
200
    }
201
202
    // If the source floating point is denormalized;
203
    if (from_exponent == 0) {
204
        // If the source mantissa is 0, the value is +/-0
205
        if (from_mantissa == 0) {
206
            to_exponent = 0;
207
            to_mantissa = 0;
208
            return target_value();
209
        }
210
211
        // If the source has more exponent bits than the target, then the largest possible
212
        // source mantissa still cannot be represented in the target denormalized value.
213
        if constexpr (From::exponentbits > To::exponentbits) {
214
            to_exponent = 0;
215
            to_mantissa = 0;
216
            return target_value();
217
        }
218
219
        // If the source and target have the same number of exponent bits, we only need to
220
        // shift the mantissa.
221
        if constexpr (From::exponentbits == To::exponentbits) {
222
            to_exponent = 0;
223
            to_mantissa = shift_mantissa(from_mantissa);
224
            return target_value();
225
        }
226
227
        // The target has more exponent bits, so our denormalized value can be represented
228
        // as a normalized value in the target floating point. Normalized values have an
229
        // implicit leading 1, so we shift the mantissa left until we find our explicit
230
        // leading 1 which is then dropped.
231
        int adjust_exponent = -1;
232
        to_mantissa = from_mantissa;
233
        do {
234
            ++adjust_exponent;
235
            to_mantissa <<= 1;
236
        } while ((to_mantissa & (1ull << From::mantissabits)) == 0);
237
        to_exponent = to_exponent_bias - from_exponent_bias - adjust_exponent;
238
239
        // Drop the most significant bit from the mantissa
240
        to_mantissa &= (1ull << From::mantissabits) - 1;
241
        to_mantissa = shift_mantissa(to_mantissa);
242
        return target_value();
243
    }
244
245
    // If the source is NaN or +/-Inf, keep it that way
246
    if (from_exponent == from_exponent_nonnumber) {
247
        to_exponent = to_exponent_nonnumber;
248
        to_mantissa = (from_mantissa == 0) ? 0 : 1;
249
        return target_value();
250
    }
251
252
    // Determine the target exponent
253
    to_exponent = to_exponent_bias - from_exponent_bias + from_exponent;
254
255
    // If the calculated exponent exceeds the target's capacity, clamp both the exponent and the
256
    // mantissa to their maximum values.
257
    if (to_exponent > to_exponent_max) {
258
        to_exponent = to_exponent_max;
259
        to_mantissa = (1ull << To::mantissabits) - 1;
260
        return target_value();
261
    }
262
263
    // If the new exponent is less than 1, we can only represent this value as a denormalized number
264
    if (to_exponent < 1) {
265
        to_exponent = 0;
266
267
        // Add a leading 1 and shift the mantissa right
268
        int adjust_exponent = 1 - to_exponent_bias - from_exponent + from_exponent_bias;
269
        to_mantissa = ((1ull << From::mantissabits) | from_mantissa) >> adjust_exponent;
270
        to_mantissa = shift_mantissa(to_mantissa);
271
        return target_value();
272
    }
273
274
    // New exponent fits; shift the mantissa to fit as well
275
    to_mantissa = shift_mantissa(from_mantissa);
276
    return target_value();
277
}
278
279
template<typename O>
280
constexpr O convert_from_native_double(double input) { return float_to_float<O>(DoubleFloatingPointBits(input)); }
281
282
template<typename O>
283
constexpr O convert_from_native_float(float input) { return float_to_float<O>(SingleFloatingPointBits(input)); }
284
285
template<typename I>
286
constexpr double convert_to_native_double(I input) { return float_to_float<DoubleFloatingPointBits>(input).as_double(); }
287
288
template<typename I>
289
constexpr float convert_to_native_float(I input) { return float_to_float<SingleFloatingPointBits>(input).as_float(); }
290
291
}
292
293
#if USING_AK_GLOBALLY
294
using AK::DoubleFloatingPointBits;
295
using AK::FloatExtractor;
296
using AK::FloatingPointBits;
297
using AK::SingleFloatingPointBits;
298
299
using AK::convert_from_native_double;
300
using AK::convert_from_native_float;
301
using AK::convert_to_native_double;
302
using AK::convert_to_native_float;
303
using AK::float_to_float;
304
#endif