3#include "native/attributes.h"
12namespace ftz {
namespace detail {
13 static const unsigned int ftz32_nan = 0x7fc00000u;
14 static const unsigned int ftz32_infinity = 0x7f800000u;
15 static const unsigned int ftz32_sign = 0x80000000u;
17 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_canonical(
unsigned int bits) {
18 unsigned int magnitude = bits & 0x7fffffffu;
21 return magnitude < 0x00800000u ? bits & ftz32_sign : bits;
23 [[nodiscard]] native_constexpr native_inline native_const
bool ftz32_isnan(
unsigned int bits) {
24 return (bits & 0x7fffffffu) > ftz32_infinity;
26 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
27 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_finish(
unsigned int bits) {
28 return ::ftz::detail::math::fp32_encode(::ftz::detail::math::fp32_ftz<Hardware>(::ftz::detail::math::fp32_decode(bits)));
30 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_neg(
unsigned int a) {
31 return a ^ ftz32_sign;
33 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_abs(
unsigned int a) {
return a & 0x7fffffffu; }
35 template <
bool HardwareFtz>
36 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_add_policy(
unsigned int a,
unsigned int b) {
39 namespace fp = ::native::detail::constexpr_float;
40 return ftz32_canonical(fp::add_bits<fp::binary32>(a, b));
44 float value = ::ftz::detail::math::fp32_decode(a) + ::ftz::detail::math::fp32_decode(b);
46 precise
float value = asfloat(a) + asfloat(b);
48 unsigned int bits = ::ftz::detail::math::fp32_encode(value);
52 if (HardwareFtz)
return bits;
53 if ((bits & 0x7fffffffu) < 0x00800000u) {
54 unsigned int aa = a & 0x7fffffffu, bb = b & 0x7fffffffu;
55 return (aa > bb ? a : aa < bb ? b : a & b) & ftz32_sign;
59 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
60 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_add(
unsigned int a,
unsigned int b) {
62 return ftz32_add_policy<Hardware>(a, b);
64 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
65 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_sub(
unsigned int a,
unsigned int b) {
68 namespace fp = ::native::detail::constexpr_float;
69 return ftz32_canonical(fp::add_bits<fp::binary32>(a, b ^ ftz32_sign));
74 return ::ftz::detail::math::fp32_encode(::ftz::detail::math::fp32_decode(a) - ::ftz::detail::math::fp32_decode(b));
76 precise
float value = asfloat(a) - asfloat(b);
80 return ftz32_add<Hardware>(a, ftz32_neg(b));
82 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_mul(
unsigned int a,
unsigned int b) {
85 namespace fp = ::native::detail::constexpr_float;
86 return ftz32_canonical(fp::mul_bits<fp::binary32>(a, b));
89 float value = ::ftz::detail::math::fp32_mul<false>(::ftz::detail::math::fp32_decode(a), ::ftz::detail::math::fp32_decode(b));
90 unsigned int bits = ::ftz::detail::math::fp32_encode(value);
91 if ((bits & 0x7fffffffu) > 0x00800000u)
return bits;
92 unsigned int sign = (a ^ b) & ftz32_sign;
93 unsigned int ea = (a >> 23) & 255u, eb = (b >> 23) & 255u;
94 if (ea == 0u || eb == 0u || ea + eb < 127u)
return sign;
95 if (ea + eb > 127u)
return sign | 0x00800000u;
99 float ma = ::ftz::detail::math::fp32_decode((a & 0x007fffffu) | 0x3f800000u);
100 float mb = ::ftz::detail::math::fp32_decode((b & 0x007fffffu) | 0x3f800000u);
101 float residual = ::ftz::detail::math::fp32_fma<false>(ma, mb,
102 ::ftz::detail::math::fp32_decode(0xbfffffffu));
103 return sign | (residual >= 0.0f ? 0x00800000u : 0u);
105 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_fma(
unsigned int a,
unsigned int b,
unsigned int c) {
108 namespace fp = ::native::detail::constexpr_float;
109 return ftz32_canonical(fp::fma_bits<fp::binary32>(a, b, c));
112 float value = ::ftz::detail::math::fp32_fma<false>(::ftz::detail::math::fp32_decode(a),
113 ::ftz::detail::math::fp32_decode(b), ::ftz::detail::math::fp32_decode(c));
114 unsigned int bits = ::ftz::detail::math::fp32_encode(value);
117 if ((bits & 0x7fffffffu) <= 0x00800000u)
118 return ::ftz::detail::math::fp32_fma_words(a, b, c).bits;
121 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
122 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_div(
unsigned int a,
unsigned int b) {
123 unsigned int aa = a & 0x7fffffffu, bb = b & 0x7fffffffu;
124 unsigned int sign = (a ^ b) & ftz32_sign;
125 if (aa > ftz32_infinity || bb > ftz32_infinity ||
126 (aa == 0u && bb == 0u) || (aa == ftz32_infinity && bb == ftz32_infinity))
128 if (aa == ftz32_infinity || bb == 0u)
return sign | ftz32_infinity;
129 if (bb == ftz32_infinity || aa == 0u)
return sign;
130 ::ftz::detail::math::approx_result r = ::ftz::detail::math::approx_div_prechecked_words(a, b);
131 return r.valid != 0u ? ftz32_finish<Hardware>(r.bits) : sign | ftz32_infinity;
133 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_sqrt(
unsigned int a) {
134 unsigned int magnitude = a & 0x7fffffffu;
135 if (magnitude == 0u)
return a;
136 if ((a & ftz32_sign) != 0u || magnitude > ftz32_infinity)
return ftz32_nan;
137 if (magnitude == ftz32_infinity)
return a;
138 return ::ftz::detail::math::approx_sqrt_prechecked_words(a).bits;
140 [[nodiscard]] native_constexpr native_inline native_const
bool ftz32_equal(
unsigned int a,
unsigned int b) {
141 return !ftz32_isnan(a) && !ftz32_isnan(b) &&
142 (a == b || ((a | b) & 0x7fffffffu) == 0u);
144 [[nodiscard]] native_constexpr native_inline native_const
bool ftz32_less(
unsigned int a,
unsigned int b) {
145 if (ftz32_isnan(a) || ftz32_isnan(b) || ftz32_equal(a, b))
return false;
146 if (((a ^ b) & ftz32_sign) != 0u)
return (a & ftz32_sign) != 0u;
147 return (a & ftz32_sign) != 0u ? a > b : a < b;
155namespace ftz {
namespace detail {
156 struct ftz32_trig_fraction {
unsigned int words[9]; };
157 struct ftz32_trig_reduction {
unsigned int residual, quadrant; };
158 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_trig_window(ftz32_trig_fraction p,
unsigned int shift) {
159 unsigned int word=shift/32u, bit=shift%32u;
160 if(word>=9u)
return 0u;
161 unsigned int result=p.words[word]>>bit;
162 if(bit!=0u && word+1u<9u)result|=p.words[word+1u]<<(32u-bit);
168 [[nodiscard]] native_constexpr native_inline native_const ftz32_trig_reduction ftz32_trig_reduce(
unsigned int magnitude) {
169 const unsigned int two_over_pi[8]={0xdebbc561u,0xfe5163abu,0x3c439041u,0xdb629599u,0xf534ddc0u,0xfc2757d1u,0x4e441529u,0xa2f9836eu};
170 unsigned int mantissa=(magnitude&0x007fffffu)|0x00800000u;
171 ftz32_trig_fraction p;
172 unsigned int carry=0u;
173 for(
unsigned int i=0u;i<8u;++i){
174 ::ftz::detail::math::unsigned_word_product product=::ftz::detail::math::unsigned_multiply_words(mantissa,two_over_pi[i]);
175 p.words[i]=product.low+carry;
176 carry=product.high+(p.words[i]<product.low?1u:0u);
179 unsigned int shift=406u-(magnitude>>23);
180 unsigned int quadrant=ftz32_trig_window(p,shift)&3u;
181 bool negative=(ftz32_trig_window(p,shift-1u)&1u)!=0u;
182 if(negative)quadrant=(quadrant+1u)&3u;
183 unsigned int limb=shift/32u, bit=shift%32u;
184 for(
unsigned int j=limb+1u;j<9u;++j)p.words[j]=0u;
185 p.words[limb]&=bit==0u?0u:(1u<<bit)-1u;
188 for(
unsigned int j=0u;j<=limb;++j){
189 unsigned int inverted=~p.words[j];
190 p.words[j]=inverted+
add;
add=(p.words[j]<inverted)?1u:0u;
192 p.words[limb]&=bit==0u?0u:(1u<<bit)-1u;
195 for(
int j=8;j>=0;--j)
if(p.words[j]!=0u){top=j*32+31-(int)::ftz::detail::math::word_leading_zeros(p.words[j]);
break;}
196 ftz32_trig_reduction result;result.quadrant=quadrant;result.residual=0u;
197 if(top<0)
return result;
198 ::ftz::detail::math::unsigned_word_product packed;
200 unsigned int discarded_bits=(
unsigned int)top-61u;
201 packed=::ftz::detail::math::word_pair(ftz32_trig_window(p,discarded_bits),ftz32_trig_window(p,discarded_bits+32u));
203 for(
unsigned int j=0u;j<discarded_bits/32u;++j)sticky=sticky||p.words[j]!=0u;
204 if(discarded_bits%32u!=0u)sticky=sticky||(p.words[discarded_bits/32u]&((1u<<(discarded_bits%32u))-1u))!=0u;
205 packed.low|=sticky?1u:0u;
206 }
else packed=::ftz::detail::math::word_pair_left(::ftz::detail::math::word_pair(p.words[0],p.words[1]),61u-(
unsigned int)top);
207 unsigned int fraction=::ftz::detail::math::fp32_pack(packed,top-(
int)shift,negative?0x80000000u:0u).bits;
208 result.residual=ftz32_mul(fraction,0x3fc90fdbu);
213namespace ftz {
namespace detail {
214 struct ftz32_sincos_bits {
unsigned int sine, cosine; };
215 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
216 [[nodiscard]] native_constexpr native_inline native_const ftz32_sincos_bits ftz32_sincos(
unsigned int bits) {
217 ftz32_sincos_bits result;
218 unsigned int magnitude=bits&0x7fffffffu;
219 if(magnitude>=ftz32_infinity){result.sine=result.cosine=ftz32_nan;
return result;}
220 bool large=magnitude>=0x46000000u;
221 ftz32_trig_reduction reduced;reduced.residual=bits;reduced.quadrant=0u;
222 if(large)reduced=ftz32_trig_reduce(magnitude);
223 float x=::ftz::detail::math::fp32_decode(reduced.residual);
225 auto [sine_value, cosine_value]=large?ftz::detail::native::sincos_reduced_ftz<Hardware>(x):ftz::detail::native::sincos_ftz<Hardware>(x);
226 unsigned int sine=::ftz::detail::math::fp32_encode(sine_value),cosine=::ftz::detail::math::fp32_encode(cosine_value);
228 ::ftz::detail::math::sincos_result pair;
229 if(large)pair=::ftz::detail::math::sincos_reduced_ftz(x);
else pair=::ftz::detail::math::sincos_ftz(x);
230 unsigned int sine=::ftz::detail::math::fp32_encode(pair.sine),cosine=::ftz::detail::math::fp32_encode(pair.cosine);
233 result.sine=(reduced.quadrant&1u)!=0u?cosine:sine;
234 result.cosine=(reduced.quadrant&1u)!=0u?sine:cosine;
235 result.sine^=((reduced.quadrant&2u)!=0u?ftz32_sign:0u)^(bits&ftz32_sign);
236 result.cosine^=(((reduced.quadrant+1u)&2u)!=0u?ftz32_sign:0u);
237 }
else{result.sine=sine;result.cosine=cosine;}
242 template <
bool Cosine,
bool Hardware>
243 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_trig_single(
unsigned int bits) {
244 unsigned int magnitude=bits&0x7fffffffu;
245 if(magnitude>=ftz32_infinity)
return ftz32_nan;
246 if(magnitude<0x46000000u) {
247 float x=::ftz::detail::math::fp32_decode(bits);
248 if constexpr(Cosine) return ::ftz::detail::math::fp32_encode(native::cos_ftz<Hardware>(x));
249 else return ::ftz::detail::math::fp32_encode(native::sin_ftz<Hardware>(x));
251 auto reduced=ftz32_trig_reduce(magnitude);
252 float x=::ftz::detail::math::fp32_decode(reduced.residual);
253 bool odd=(reduced.quadrant&1u)!=0u;
255 if constexpr(Cosine) value=odd?native::sin_reduced_ftz<Hardware>(x):native::cos_reduced_ftz<Hardware>(x);
256 else value=odd?native::cos_reduced_ftz<Hardware>(x):native::sin_reduced_ftz<Hardware>(x);
257 unsigned int result=::ftz::detail::math::fp32_encode(value);
258 if constexpr(Cosine)
return result^(((reduced.quadrant+1u)&2u)!=0u?ftz32_sign:0u);
259 else return result^(((reduced.quadrant&2u)!=0u?ftz32_sign:0u)^(bits&ftz32_sign));
262 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
263 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_sin(
unsigned int bits){
265 return ftz32_trig_single<false,Hardware>(bits);
267 return ftz32_sincos<Hardware>(bits).sine;
270 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
271 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_cos(
unsigned int bits){
273 return ftz32_trig_single<true,Hardware>(bits);
275 return ftz32_sincos<Hardware>(bits).cosine;
278 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
279 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_tanh(
unsigned int bits){return ::ftz::detail::math::tanh_words<Hardware>(bits);}
280 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
281 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_log(
unsigned int bits){return ::ftz::detail::math::log_words<Hardware>(bits).bits;}
282 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
283 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_log1p(
unsigned int bits){return ::ftz::detail::math::log1p_words<Hardware>(bits).bits;}
284 template <
unsigned int Degree = 6>
285 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_exp(
unsigned int bits){
287 return ::ftz::detail::math::fp32_encode(ftz::detail::native::exp_ftz<Degree>(
288 ftz::detail::native::fp32x1(::ftz::detail::math::fp32_decode(bits))).value);
290 unsigned int magnitude=bits&0x7fffffffu;
291 if(magnitude>ftz32_infinity)
return ftz32_nan;
292 if(magnitude==ftz32_infinity)
return (bits&ftz32_sign)!=0u?0u:ftz32_infinity;
293 return ::ftz::detail::math::exp_value<Degree>(bits).x;
296 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
297 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_expm1(
unsigned int bits){
298 unsigned int magnitude=bits&0x7fffffffu;
299 if(magnitude>ftz32_infinity)
return ftz32_nan;
300 if(magnitude==ftz32_infinity)
return (bits&ftz32_sign)!=0u?0xbf800000u:ftz32_infinity;
301 if((bits&ftz32_sign)==0u && magnitude>0x3f800000u)
return ftz32_sub<Hardware>(ftz32_exp(bits),0x3f800000u);
303 return ::ftz::detail::math::fp32_encode(ftz::detail::native::expm1_checked<Hardware>(::ftz::detail::math::fp32_decode(bits)).value);
305 return ::ftz::detail::math::fp32_encode(::ftz::detail::math::expm1_checked(::ftz::detail::math::fp32_decode(bits)).value);
308 template <
bool Hardware = FTZ_FP32_HARDWARE_FTZ != 0>
309 [[nodiscard]] native_constexpr native_inline native_const
unsigned int ftz32_atan2(
unsigned int y,
unsigned int x){
310 unsigned int ay=y&0x7fffffffu,ax=x&0x7fffffffu,sign=y&ftz32_sign;
311 if(ay>ftz32_infinity || ax>ftz32_infinity)
return ftz32_nan;
312 if(ay==0u)
return ((x&ftz32_sign)!=0u?0x40490fdbu:0u)|sign;
313 if(ay==ftz32_infinity){
314 if(ax==ftz32_infinity)
return ((x&ftz32_sign)!=0u?0x4016cbe4u:0x3f490fdbu)|sign;
315 return 0x3fc90fdbu|sign;
317 if(ax==ftz32_infinity)
return ((x&ftz32_sign)!=0u?0x40490fdbu:0u)|sign;
318 return ::ftz::detail::math::atan2_words<Hardware>(y,x).bits;
Reciprocal-refined division, square root and reciprocal square root.
Finite atan2 with normalized division and a fixed Horner polynomial.
FP32 bit casts, precise arithmetic and signed flush-to-zero normalization.
constexpr std::array< R, N > add(std::array< R, N > const &a, std::array< R, N > const &b) noexcept
Adds matching register arrays; a non-array operand is converted once and broadcast,...
Deterministic polynomial log and log1p.
Host implementations of FTZ math for scalar and SIMD register packs.
Deterministic piecewise-polynomial tanh.
Native 64-bit exact binary32 packing, multiplication, and FMA repair.