1 | #pragma once |
2 | |
3 | #include <cmath> |
4 | |
5 | // Android NDK platform < 21 with libstdc++ has spotty C++11 support. |
6 | // Various hacks in this header allow the rest of the codebase to use |
7 | // standard APIs. |
8 | #if (defined(__ANDROID__) && __ANDROID_API__ < 21 && defined(__GLIBCXX__)) || \ |
9 | defined(__NEWLIB__) |
10 | #include <stdexcept> |
11 | |
12 | namespace std { |
13 | // Import double versions of these functions from the global namespace. |
14 | using ::acosh; |
15 | using ::asinh; |
16 | using ::atanh; |
17 | using ::erf; |
18 | using ::erfc; |
19 | using ::expm1; |
20 | using ::lgamma; |
21 | using ::log1p; |
22 | using ::nearbyint; |
23 | using ::round; |
24 | using ::tgamma; |
25 | using ::trunc; |
26 | using ::truncf; |
27 | |
28 | // Define float versions the same way as more recent libstdc++ |
29 | inline float acosh(float x) { |
30 | return __builtin_acoshf(x); |
31 | } |
32 | inline float asinh(float x) { |
33 | return __builtin_asinhf(x); |
34 | } |
35 | inline float atanh(float x) { |
36 | return __builtin_atanhf(x); |
37 | } |
38 | inline float copysign(float x, float y) { |
39 | return __builtin_copysignf(x, y); |
40 | } |
41 | inline float erf(float x) { |
42 | return __builtin_erff(x); |
43 | } |
44 | inline float erfc(float x) { |
45 | return __builtin_erfcf(x); |
46 | } |
47 | inline float expm1(float x) { |
48 | return __builtin_expm1f(x); |
49 | } |
50 | inline float fmax(float x, float y) { |
51 | return __builtin_fmaxf(x, y); |
52 | } |
53 | inline float fmin(float x, float y) { |
54 | return __builtin_fminf(x, y); |
55 | } |
56 | inline float lgamma(float x) { |
57 | return __builtin_lgammaf(x); |
58 | } |
59 | inline float log1p(float x) { |
60 | return __builtin_log1pf(x); |
61 | } |
62 | inline float nearbyint(float x) { |
63 | return __builtin_nearbyintf(x); |
64 | } |
65 | inline float remainder(float x, float y) { |
66 | return __builtin_remainderf(x, y); |
67 | } |
68 | inline float round(float x) { |
69 | return __builtin_roundf(x); |
70 | } |
71 | inline float tgamma(float x) { |
72 | return __builtin_tgammaf(x); |
73 | } |
74 | inline float trunc(float x) { |
75 | return __builtin_truncf(x); |
76 | } |
77 | |
78 | // __builtin_nexttoward isn't doesn't work. It appears to try to |
79 | // link against the global nexttoward function, which is not present |
80 | // prior to API 18. Just bail for now. |
81 | inline float nexttoward(float x, long double y) { |
82 | throw std::runtime_error("std::nexttoward is not present on older Android" ); |
83 | } |
84 | inline double nexttoward(double x, long double y) { |
85 | throw std::runtime_error("std::nexttoward is not present on older Android" ); |
86 | } |
87 | |
88 | #if !defined(__NEWLIB__) |
89 | // TODO: this function needs to be implemented and tested. Currently just throw |
90 | // an error. |
91 | inline float hypot(float x, float y) { |
92 | throw std::runtime_error("std::hypot is not implemented on older Android" ); |
93 | } |
94 | inline double hypot(double x, double y) { |
95 | throw std::runtime_error("std::hypot is not implemented on older Android" ); |
96 | } |
97 | #else |
98 | inline float hypot(float x, float y) { |
99 | return hypot((double)x, (double)y); |
100 | } |
101 | #endif |
102 | |
103 | // TODO: this function needs to be implemented and tested. Currently just throw |
104 | // an error. |
105 | inline float igamma(float x, float y) { |
106 | throw std::runtime_error("igamma is not implemented on older Android" ); |
107 | } |
108 | inline double igamma(double x, double y) { |
109 | throw std::runtime_error("igamma is not implemented on older Android" ); |
110 | } |
111 | inline float igammac(float x, float y) { |
112 | throw std::runtime_error("igammac is not implemented on older Android" ); |
113 | } |
114 | inline double igammac(double x, double y) { |
115 | throw std::runtime_error("igammac is not implemented on older Android" ); |
116 | } |
117 | |
118 | // Note: std::signbit returns true for negative zero (-0), but this |
119 | // implementation returns false. |
120 | inline bool signbit(float x) { |
121 | return x < 0; |
122 | } |
123 | inline bool signbit(double x) { |
124 | return x < 0; |
125 | } |
126 | inline bool signbit(long double x) { |
127 | return x < 0; |
128 | } |
129 | |
130 | #if !defined(__NEWLIB__) |
131 | // TODO: this function needs to be implemented and tested. Currently just throw |
132 | // an error. |
133 | inline float nextafter(float x, float y) { |
134 | throw std::runtime_error( |
135 | "std::nextafter is not implemented on older Android" ); |
136 | } |
137 | inline double nextafter(double x, double y) { |
138 | throw std::runtime_error( |
139 | "std::nextafter is not implemented on older Android" ); |
140 | } |
141 | #else |
142 | inline float nextafter(float x, float y) { |
143 | return nextafter((double)x, (double)y); |
144 | } |
145 | #endif |
146 | |
147 | #if !defined(__NEWLIB__) |
148 | // TODO: this function needs to be implemented and tested. Currently just throw |
149 | // an error. |
150 | inline float exp2(float x) { |
151 | throw std::runtime_error("std::exp2 is not implemented on older Android" ); |
152 | } |
153 | inline double exp2(double x) { |
154 | throw std::runtime_error("std::exp2 is not implemented on older Android" ); |
155 | } |
156 | #else |
157 | inline float exp2(float x) { |
158 | return exp2((double)x); |
159 | } |
160 | #endif |
161 | |
162 | // Define integral versions the same way as more recent libstdc++ |
163 | template <typename T> |
164 | typename std::enable_if<std::is_integral<T>::value, double>::type acosh(T x) { |
165 | return __builtin_acosh(x); |
166 | } |
167 | template <typename T> |
168 | typename std::enable_if<std::is_integral<T>::value, double>::type asinh(T x) { |
169 | return __builtin_asinh(x); |
170 | } |
171 | template <typename T> |
172 | typename std::enable_if<std::is_integral<T>::value, double>::type atanh(T x) { |
173 | return __builtin_atanh(x); |
174 | } |
175 | template <typename T> |
176 | typename std::enable_if<std::is_integral<T>::value, double>::type erf(T x) { |
177 | return __builtin_erf(x); |
178 | } |
179 | template <typename T> |
180 | typename std::enable_if<std::is_integral<T>::value, double>::type erfc(T x) { |
181 | return __builtin_erfc(x); |
182 | } |
183 | template <typename T> |
184 | typename std::enable_if<std::is_integral<T>::value, double>::type expm1(T x) { |
185 | return __builtin_expm1(x); |
186 | } |
187 | template <typename T> |
188 | typename std::enable_if<std::is_integral<T>::value, double>::type lgamma(T x) { |
189 | return __builtin_lgamma(x); |
190 | } |
191 | template <typename T> |
192 | typename std::enable_if<std::is_integral<T>::value, double>::type log1p(T x) { |
193 | return __builtin_log1p(x); |
194 | } |
195 | template <typename T> |
196 | typename std::enable_if<std::is_integral<T>::value, double>::type nearbyint( |
197 | T x) { |
198 | return __builtin_nearbyint(x); |
199 | } |
200 | template <typename T> |
201 | typename std::enable_if<std::is_integral<T>::value, double>::type round(T x) { |
202 | return __builtin_round(x); |
203 | } |
204 | template <typename T> |
205 | typename std::enable_if<std::is_integral<T>::value, double>::type tgamma(T x) { |
206 | return __builtin_tgamma(x); |
207 | } |
208 | template <typename T> |
209 | typename std::enable_if<std::is_integral<T>::value, double>::type trunc(T x) { |
210 | return __builtin_trunc(x); |
211 | } |
212 | |
213 | // Convoluted definition of these binary functions for overloads other than |
214 | // (float,float) and (double,double). Using a template from __gnu_cxx |
215 | // is dirty, but this code is only enabled on a dead platform, so there |
216 | // shouldn't be any risk of it breaking due to updates. |
217 | template <typename T, typename U> |
218 | typename __gnu_cxx::__promote_2<T, U>::__type fmax(T x, U y) { |
219 | typedef typename __gnu_cxx::__promote_2<T, U>::__type type; |
220 | return fmax(type(x), type(y)); |
221 | } |
222 | template <typename T, typename U> |
223 | typename __gnu_cxx::__promote_2<T, U>::__type fmin(T x, U y) { |
224 | typedef typename __gnu_cxx::__promote_2<T, U>::__type type; |
225 | return fmin(type(x), type(y)); |
226 | } |
227 | template <typename T, typename U> |
228 | typename __gnu_cxx::__promote_2<T, U>::__type copysign(T x, U y) { |
229 | typedef typename __gnu_cxx::__promote_2<T, U>::__type type; |
230 | return copysign(type(x), type(y)); |
231 | } |
232 | template <typename T, typename U> |
233 | typename __gnu_cxx::__promote_2<T, U>::__type remainder(T x, U y) { |
234 | typedef typename __gnu_cxx::__promote_2<T, U>::__type type; |
235 | return remainder(type(x), type(y)); |
236 | } |
237 | |
238 | // log2 is a macro on Android API < 21, so we need to define it ourselves. |
239 | inline float log2(float arg) { |
240 | return ::log(arg) / ::log(2.0); |
241 | } |
242 | #if !defined(__NEWLIB__) |
243 | inline double log2(double arg) { |
244 | return ::log(arg) / ::log(2.0); |
245 | } |
246 | #endif |
247 | inline long double log2(long double arg) { |
248 | return ::log(arg) / ::log(2.0); |
249 | } |
250 | template <typename T> |
251 | typename std::enable_if<std::is_integral<T>::value, double>::type log2(T x) { |
252 | return ::log(x) / ::log(2.0); |
253 | } |
254 | } // namespace std |
255 | |
256 | #endif |
257 | |