Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/tools/source/misc/fix16.cxx
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2
/*
3
 * libfixmath is Copyright (c) 2011-2021 Flatmush <Flatmush@gmail.com>,
4
 * Petteri Aimonen <Petteri.Aimonen@gmail.com>, & libfixmath AUTHORS
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in all
14
 * copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
 * SOFTWARE.
23
 */
24
25
#include <tools/fix16.hxx>
26
27
#include <bit>
28
29
const fix16_t fix16_minimum = 0x80000000; /*!< the minimum value of fix16_t */
30
const fix16_t fix16_overflow = 0x80000000; /*!< the value used to indicate overflows */
31
32
static inline uint32_t fix_abs(fix16_t in)
33
259M
{
34
259M
    if (in == fix16_minimum)
35
86.3k
    {
36
        // minimum negative number has same representation as
37
        // its absolute value in unsigned
38
86.3k
        return 0x80000000;
39
86.3k
    }
40
259M
    else
41
259M
    {
42
259M
        return (in >= 0) ? in : -in;
43
259M
    }
44
259M
}
45
46
/* 64-bit implementation for fix16_mul. Fastest version for e.g. ARM Cortex M3.
47
 * Performs a 32*32 -> 64bit multiplication. The middle 32 bits are the result,
48
 * bottom 16 bits are used for rounding, and upper 16 bits are used for overflow
49
 * detection.
50
 */
51
52
fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1)
53
194M
{
54
194M
    int64_t product = static_cast<int64_t>(inArg0) * inArg1;
55
56
    // The upper 17 bits should all be the same (the sign).
57
194M
    uint32_t upper = (product >> 47);
58
59
194M
    if (product < 0)
60
12.6M
    {
61
12.6M
        if (~upper)
62
62.8k
            return fix16_overflow;
63
64
        // This adjustment is required in order to round -1/2 correctly
65
12.6M
        product--;
66
12.6M
    }
67
181M
    else
68
181M
    {
69
181M
        if (upper)
70
172k
            return fix16_overflow;
71
181M
    }
72
73
194M
    fix16_t result = (product >> 16) & 0xFFFF;
74
194M
    result += (product & 0x8000) >> 15;
75
76
194M
    return result;
77
194M
}
78
79
13.6M
static uint32_t mask(int bits) { return (1U << bits) - 1; }
80
81
/* 32-bit implementation of fix16_div. Fastest version for e.g. ARM Cortex M3.
82
 * Performs 32-bit divisions repeatedly to reduce the remainder. For this to
83
 * be efficient, the processor has to have 32-bit hardware division.
84
 */
85
fix16_t fix16_div(fix16_t a, fix16_t b)
86
129M
{
87
    // This uses a hardware 32/32 bit division multiple times, until we have
88
    // computed all the bits in (a<<17)/b. Usually this takes 1-3 iterations.
89
90
129M
    if (b == 0)
91
0
        return fix16_minimum;
92
93
129M
    uint32_t remainder = fix_abs(a);
94
129M
    uint32_t divider = fix_abs(b);
95
129M
    uint64_t quotient = 0;
96
129M
    int bit_pos = 17;
97
98
    // Kick-start the division a bit.
99
    // This improves speed in the worst-case scenarios where N and D are large
100
    // It gets a lower estimate for the result by N/(D >> 17 + 1).
101
129M
    if (divider & 0xFFF00000)
102
0
    {
103
0
        uint32_t shifted_div = (divider >> 17) + 1;
104
0
        quotient = remainder / shifted_div;
105
0
        uint64_t tmp = (quotient * static_cast<uint64_t>(divider)) >> 17;
106
0
        remainder -= static_cast<uint32_t>(tmp);
107
0
    }
108
109
    // If the divider is divisible by 2^n, take advantage of it.
110
505M
    while (!(divider & 0xF) && bit_pos >= 4)
111
375M
    {
112
375M
        divider >>= 4;
113
375M
        bit_pos -= 4;
114
375M
    }
115
116
144M
    while (remainder > 0 && bit_pos >= 0)
117
15.1M
    {
118
        // Shift remainder as much as we can without overflowing
119
15.1M
        int shift = std::countl_zero(remainder);
120
15.1M
        if (shift > bit_pos)
121
12.8M
            shift = bit_pos;
122
15.1M
        if (shift)
123
13.6M
        {
124
13.6M
            remainder = (remainder & mask(32 - shift)) << shift;
125
13.6M
            bit_pos -= shift;
126
13.6M
        }
127
128
15.1M
        uint32_t div = remainder / divider;
129
15.1M
        remainder = remainder % divider;
130
15.1M
        quotient += static_cast<uint64_t>(div) << bit_pos;
131
132
15.1M
        if (div & ~(0xFFFFFFFF >> bit_pos))
133
11.0k
            return fix16_overflow;
134
135
15.1M
        remainder <<= 1;
136
15.1M
        bit_pos--;
137
15.1M
    }
138
139
    // Quotient is always positive so rounding is easy
140
129M
    quotient++;
141
142
129M
    fix16_t result = quotient >> 1;
143
144
    // Figure out the sign of the result
145
129M
    if ((a ^ b) & 0x80000000)
146
75.2k
    {
147
75.2k
        if (result == fix16_minimum)
148
0
            return fix16_overflow;
149
150
75.2k
        result = -result;
151
75.2k
    }
152
153
129M
    return result;
154
129M
}
155
156
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */