/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: */ |