LibreOffice Module tools (master) 1
fix16.cxx
Go to the documentation of this file.
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
27const fix16_t fix16_minimum = 0x80000000;
28const fix16_t fix16_overflow = 0x80000000;
30static inline uint32_t fix_abs(fix16_t in)
31{
32 if (in == fix16_minimum)
33 {
34 // minimum negative number has same representation as
35 // its absolute value in unsigned
36 return 0x80000000;
37 }
38 else
39 {
40 return (in >= 0) ? in : -in;
41 }
42}
43
44/* 64-bit implementation for fix16_mul. Fastest version for e.g. ARM Cortex M3.
45 * Performs a 32*32 -> 64bit multiplication. The middle 32 bits are the result,
46 * bottom 16 bits are used for rounding, and upper 16 bits are used for overflow
47 * detection.
48 */
49
51{
52 int64_t product = static_cast<int64_t>(inArg0) * inArg1;
53
54 // The upper 17 bits should all be the same (the sign).
55 uint32_t upper = (product >> 47);
56
57 if (product < 0)
58 {
59 if (~upper)
60 return fix16_overflow;
61
62 // This adjustment is required in order to round -1/2 correctly
63 product--;
64 }
65 else
66 {
67 if (upper)
68 return fix16_overflow;
69 }
70
71 fix16_t result = product >> 16;
72 result += (product & 0x8000) >> 15;
73
74 return result;
75}
76
77/* 32-bit implementation of fix16_div. Fastest version for e.g. ARM Cortex M3.
78 * Performs 32-bit divisions repeatedly to reduce the remainder. For this to
79 * be efficient, the processor has to have 32-bit hardware division.
80 */
81#ifdef __GNUC__
82// Count leading zeros, using processor-specific instruction if available.
83#define clz(x) (__builtin_clzl(x) - (8 * sizeof(long) - 32))
84#else
85static uint8_t clz(uint32_t x)
86{
87 uint8_t result = 0;
88 if (x == 0)
89 return 32;
90 while (!(x & 0xF0000000))
91 {
92 result += 4;
93 x <<= 4;
94 }
95 while (!(x & 0x80000000))
96 {
97 result += 1;
98 x <<= 1;
99 }
100 return result;
101}
102#endif
103
105{
106 // This uses a hardware 32/32 bit division multiple times, until we have
107 // computed all the bits in (a<<17)/b. Usually this takes 1-3 iterations.
108
109 if (b == 0)
110 return fix16_minimum;
111
112 uint32_t remainder = fix_abs(a);
113 uint32_t divider = fix_abs(b);
114 uint64_t quotient = 0;
115 int bit_pos = 17;
116
117 // Kick-start the division a bit.
118 // This improves speed in the worst-case scenarios where N and D are large
119 // It gets a lower estimate for the result by N/(D >> 17 + 1).
120 if (divider & 0xFFF00000)
121 {
122 uint32_t shifted_div = (divider >> 17) + 1;
123 quotient = remainder / shifted_div;
124 uint64_t tmp = (quotient * static_cast<uint64_t>(divider)) >> 17;
125 remainder -= static_cast<uint32_t>(tmp);
126 }
127
128 // If the divider is divisible by 2^n, take advantage of it.
129 while (!(divider & 0xF) && bit_pos >= 4)
130 {
131 divider >>= 4;
132 bit_pos -= 4;
133 }
134
135 while (remainder && bit_pos >= 0)
136 {
137 // Shift remainder as much as we can without overflowing
138 int shift = clz(remainder);
139 if (shift > bit_pos)
140 shift = bit_pos;
141 remainder <<= shift;
142 bit_pos -= shift;
143
144 uint32_t div = remainder / divider;
145 remainder = remainder % divider;
146 quotient += static_cast<uint64_t>(div) << bit_pos;
147
148 if (div & ~(0xFFFFFFFF >> bit_pos))
149 return fix16_overflow;
150
151 remainder <<= 1;
152 bit_pos--;
153 }
154
155 // Quotient is always positive so rounding is easy
156 quotient++;
157
158 fix16_t result = quotient >> 1;
159
160 // Figure out the sign of the result
161 if ((a ^ b) & 0x80000000)
162 {
163 if (result == fix16_minimum)
164 return fix16_overflow;
165
166 result = -result;
167 }
168
169 return result;
170}
171
172/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
float x
const fix16_t fix16_minimum
Definition: fix16.cxx:27
fix16_t fix16_div(fix16_t a, fix16_t b)
Definition: fix16.cxx:104
fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1)
Definition: fix16.cxx:50
static uint32_t fix_abs(fix16_t in)
Definition: fix16.cxx:30
static uint8_t clz(uint32_t x)
Definition: fix16.cxx:85
const fix16_t fix16_overflow
Definition: fix16.cxx:28
int32_t fix16_t
Definition: fix16.hxx:30
uno_Any a
int shift
double div(const double &fNumerator, const double &fDenominator)
Any result