ftz 0.0.1
Fast, reproducible floating-point arithmetic
Loading...
Searching...
No Matches
approx.h
Go to the documentation of this file.
1#pragma once
2#include "ftz/config.h"
3#include "ftz/math/float.h"
4namespace ftz { namespace detail { namespace math {
5 struct approx_result { unsigned int bits, valid; };
6 native_constexpr inline unsigned int approx_flush(unsigned int x) {
7 return (x & 0x7f800000u) == 0u ? x & 0x80000000u : x;
8 }
9 native_constexpr inline bool approx_finite(unsigned int x) { return (x & 0x7fffffffu) < 0x7f800000u; }
10 // Scale a positive normal intermediate by a power of two without floating
11 // underflow. RNE at the minnormal boundary precedes signed output FTZ.
12 native_constexpr inline approx_result approx_scale(unsigned int word, int shift, unsigned int sign) {
13 approx_result r; r.bits=0u; r.valid=1u;
14 int exponent=(int)(word>>23)+shift;
15 if (exponent>=255) { r.valid=0u; return r; }
16 if (exponent<=0) {
17 r.bits=sign | ((exponent==0 && (word&0x007fffffu)==0x007fffffu) ? 0x00800000u : 0u);
18 return r;
19 }
20 r.bits=sign | ((unsigned int)exponent<<23) | (word&0x007fffffu);
21 return r;
22 }
23 // The graph's normalized operands stay normal under every supported FTZ mode.
24 // It intentionally approximates the mathematical operation; version 1 is not
25 // an exact-RNE division/square-root contract or a hardware-estimate wrapper.
26 native_constexpr inline approx_result approx_apply(unsigned int operation, unsigned int a, unsigned int b) {
27 approx_result invalid; invalid.bits=invalid.valid=0u;
28 if (operation>3u || !approx_finite(a) ||
29 (operation==3u ? !approx_finite(b) : b!=0u)) return invalid;
30 a=approx_flush(a); b=approx_flush(b);
31 unsigned int aa=a&0x7fffffffu, bb=b&0x7fffffffu;
32 unsigned int sign=a&0x80000000u;
33 if (operation==0u && aa==0u) return invalid;
34 if ((operation==1u || operation==2u) && sign!=0u && aa!=0u) return invalid;
35 if (operation==2u && aa==0u) return invalid;
36 if (operation==3u && bb==0u) return invalid;
37 if ((operation==1u || operation==3u) && aa==0u) {
38 approx_result result;
39 result.bits=operation==1u ? a : (a^b)&0x80000000u;
40 result.valid=1u;
41 return result;
42 }
43 int exponent_a=(int)(aa>>23)-127;
44 unsigned int ma=0x3f800000u | (aa&0x007fffffu);
45 float r, p, e, h;
46 int shift;
47 if (operation==0u || operation==3u) {
48 unsigned int mb=operation==3u ? 0x3f800000u|(bb&0x007fffffu) : ma;
49 float m=fp32_decode(mb);
50 r=fp32_decode(0x7ef311c3u-mb);
51 e=fp32_fma<false>(-m,r,1.0f); r=fp32_fma<false>(r,e,r);
52 e=fp32_fma<false>(-m,r,1.0f); r=fp32_fma<false>(r,e,r);
53 e=fp32_fma<false>(-m,r,1.0f); r=fp32_fma<false>(r,e,r);
54 if (operation==3u) {
55 r=fp32_mul<false>(fp32_decode(ma),r);
56 shift=exponent_a-((int)(bb>>23)-127); sign=(a^b)&0x80000000u;
57 } else shift=-exponent_a;
58 } else {
59 unsigned int parity=1u-((aa>>23)&1u);
60 unsigned int mword=ma+(parity<<23);
61 float m=fp32_decode(mword);
62 r=fp32_decode(0x5f375a86u-(mword>>1));
63 p=fp32_mul<false>(m,r); e=fp32_fma<false>(-p,r,1.0f); h=fp32_mul<false>(0.5f,r); r=fp32_fma<false>(h,e,r);
64 p=fp32_mul<false>(m,r); e=fp32_fma<false>(-p,r,1.0f); h=fp32_mul<false>(0.5f,r); r=fp32_fma<false>(h,e,r);
65 p=fp32_mul<false>(m,r); e=fp32_fma<false>(-p,r,1.0f); h=fp32_mul<false>(0.5f,r); r=fp32_fma<false>(h,e,r);
66 shift=(exponent_a-(int)parity)/2;
67 if (operation==1u) r=fp32_mul<false>(m,r); else shift=-shift;
68 sign=0u;
69 }
70 return approx_scale(fp32_encode(r),shift,sign);
71 }
72 native_constexpr inline approx_result approx_reciprocal(unsigned int a) { return approx_apply(0u,a,0u); }
73 native_constexpr inline approx_result approx_sqrt(unsigned int a) { return approx_apply(1u,a,0u); }
74 native_constexpr inline approx_result approx_rsqrt(unsigned int a) { return approx_apply(2u,a,0u); }
75 native_constexpr inline approx_result approx_divide(unsigned int a,unsigned int b) { return approx_apply(3u,a,b); }
76 // Internal precondition: finite normal or signed-zero operands.
77 native_constexpr inline approx_result approx_div_prechecked_words(unsigned int a, unsigned int b) {
78 unsigned int aa=a&0x7fffffffu, bb=b&0x7fffffffu;
79 approx_result result; result.bits=result.valid=0u;
80 if (bb==0u) return result;
81 unsigned int sign=(a^b)&0x80000000u;
82 if (aa==0u) { result.bits=sign; result.valid=1u; return result; }
83 unsigned int ma=0x3f800000u|(aa&0x007fffffu);
84 unsigned int mb=0x3f800000u|(bb&0x007fffffu);
85 float m=fp32_decode(mb);
86 float r=fp32_decode(0x7ef311c3u-mb);
87 float e=fp32_fma<false>(-m,r,1.0f); r=fp32_fma<false>(r,e,r);
88 e=fp32_fma<false>(-m,r,1.0f); r=fp32_fma<false>(r,e,r);
89 e=fp32_fma<false>(-m,r,1.0f); r=fp32_fma<false>(r,e,r);
90 r=fp32_mul<false>(fp32_decode(ma),r);
91 int shift=((int)(aa>>23)-127)-((int)(bb>>23)-127);
92 return approx_scale(fp32_encode(r),shift,sign);
93 }
94 native_constexpr inline approx_result approx_sqrt_prechecked_words(unsigned int a) {
95 unsigned int aa=a&0x7fffffffu;
96 approx_result result; result.bits=result.valid=0u;
97 if (aa==0u) { result.bits=a; result.valid=1u; return result; }
98 if ((a&0x80000000u)!=0u) return result;
99 unsigned int ma=0x3f800000u|(aa&0x007fffffu);
100 unsigned int parity=1u-((aa>>23)&1u);
101 unsigned int mword=ma+(parity<<23);
102 float m=fp32_decode(mword);
103 float r=fp32_decode(0x5f375a86u-(mword>>1));
104 float p=fp32_mul<false>(m,r), e=fp32_fma<false>(-p,r,1.0f), h=fp32_mul<false>(0.5f,r); r=fp32_fma<false>(h,e,r);
105 p=fp32_mul<false>(m,r); e=fp32_fma<false>(-p,r,1.0f); h=fp32_mul<false>(0.5f,r); r=fp32_fma<false>(h,e,r);
106 p=fp32_mul<false>(m,r); e=fp32_fma<false>(-p,r,1.0f); h=fp32_mul<false>(0.5f,r); r=fp32_fma<false>(h,e,r);
107 r=fp32_mul<false>(m,r);
108 int shift=(((int)(aa>>23)-127)-(int)parity)/2;
109 return approx_scale(fp32_encode(r),shift,0u);
110 }
111}}}
112
FP32 bit casts, precise arithmetic and signed flush-to-zero normalization.