1 #pragma once
2 
3 /** @file fixed.h  Fixed-point 48.16 math routines.
4 
5 @authors Copyright (c) 2020 Jaakko Keränen <jaakko.keranen@iki.fi>
6 
7 @par License
8 
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
11 
12 1. Redistributions of source code must retain the above copyright notice, this
13    list of conditions and the following disclaimer.
14 2. Redistributions in binary form must reproduce the above copyright notice,
15    this list of conditions and the following disclaimer in the documentation
16    and/or other materials provided with the distribution.
17 
18 <small>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</small>
28 */
29 
30 #include "defs.h"
31 #include "random.h"
32 
33 #include <math.h>
34 
35 iDeclareType(Fixed)
36 
37 typedef int64_t     iFixed64;
38 #if defined(__SIZEOF_INT128__)
39 typedef __int128_t  iFixedLong;
40 #else
41 typedef int64_t     iFixedLong; /* oops, will overflow */
42 #endif
43 
44 struct Impl_Fixed {
45     union {
46         iFixed64 v;
47         struct {
48             uint64_t frac : 16;
49             uint64_t wnum : 47;
50             uint64_t sign : 1;
51         } comp;
52     };
53 };
54 
55 #define iFixedFracBits  16
56 #define iFixedUnit      (1 << iFixedFracBits)
57 #define iFixedMaxWNum   ((((iFixed64) 1) << 47) - 1)
58 
zero_Fixed(void)59 iLocalDef iFixed zero_Fixed(void) {
60     return (iFixed){ .v = 0 };
61 }
62 
one_Fixed(void)63 iLocalDef iFixed one_Fixed(void) {
64     return (iFixed){ .v = iFixedUnit };
65 }
66 
half_Fixed(void)67 iLocalDef iFixed half_Fixed(void) {
68     return (iFixed){ .v = iFixedUnit >> 1 };
69 }
70 
init_Fixed(iFixed64 fp)71 iLocalDef iFixed init_Fixed(iFixed64 fp) {
72     return (iFixed){ .v = fp };
73 }
74 
initi_Fixed(int32_t i)75 iLocalDef iFixed initi_Fixed(int32_t i) {
76     return (iFixed){ .v = i << iFixedFracBits };
77 }
78 
initf_Fixed(float f)79 iLocalDef iFixed initf_Fixed(float f) {
80     return (iFixed){ .v = (iFixed64) (f * iFixedUnit) };
81 }
82 
initd_Fixed(double d)83 iLocalDef iFixed initd_Fixed(double d) {
84     return (iFixed){ .v = (iFixed64) (d * iFixedUnit) };
85 }
86 
add_Fixed(const iFixed a,const iFixed b)87 iLocalDef iFixed add_Fixed(const iFixed a, const iFixed b) {
88     return (iFixed){ .v = a.v + b.v };
89 }
90 
addv_Fixed(iFixed * a,const iFixed b)91 iLocalDef void addv_Fixed(iFixed *a, const iFixed b) {
92     a->v += b.v;
93 }
94 
sub_Fixed(const iFixed a,const iFixed b)95 iLocalDef iFixed sub_Fixed(const iFixed a, const iFixed b) {
96     return (iFixed){ .v = a.v - b.v };
97 }
98 
subv_Fixed(iFixed * a,const iFixed b)99 iLocalDef void subv_Fixed(iFixed *a, const iFixed b) {
100     a->v -= b.v;
101 }
102 
mul_Fixed(const iFixed a,const iFixed b)103 iLocalDef iFixed mul_Fixed(const iFixed a, const iFixed b) {
104     return init_Fixed((((iFixedLong) a.v * (iFixedLong) b.v) >> iFixedFracBits));
105 }
106 
mulv_Fixed(iFixed * a,const iFixed b)107 iLocalDef void mulv_Fixed(iFixed *a, const iFixed b) {
108     *a = mul_Fixed(*a, b);
109 }
110 
muli_Fixed(const iFixed a,int i)111 iLocalDef iFixed muli_Fixed(const iFixed a, int i) {
112     return init_Fixed(a.v * i);
113 }
114 
mulf_Fixed(const iFixed a,float f)115 iLocalDef iFixed mulf_Fixed(const iFixed a, float f) {
116     return init_Fixed((iFixed64) (a.v * f));
117 }
118 
div_Fixed(const iFixed a,const iFixed b)119 iLocalDef iFixed div_Fixed(const iFixed a, const iFixed b) {
120     return init_Fixed(((iFixedLong) a.v << iFixedFracBits) / (iFixedLong) b.v);
121 }
122 
divv_Fixed(iFixed * a,const iFixed b)123 iLocalDef void divv_Fixed(iFixed *a, const iFixed b) {
124     *a = div_Fixed(*a, b);
125 }
126 
divi_Fixed(const iFixed a,int i)127 iLocalDef iFixed divi_Fixed(const iFixed a, int i) {
128     return div_Fixed(a, initi_Fixed(i));
129 }
130 
divf_Fixed(const iFixed a,float f)131 iLocalDef iFixed divf_Fixed(const iFixed a, float f) {
132     return div_Fixed(a, initf_Fixed(f));
133 }
134 
value_Fixed(const iFixed a)135 iLocalDef iFixed64 value_Fixed(const iFixed a) {
136     return a.v;
137 }
138 
i64_Fixed(const iFixed a)139 iLocalDef int64_t i64_Fixed(const iFixed a) {
140     return a.v >> iFixedFracBits;
141 }
142 
i32_Fixed(const iFixed a)143 iLocalDef int32_t i32_Fixed(const iFixed a) {
144     return (int32_t) a.v >> iFixedFracBits;
145 }
146 
f64_Fixed(const iFixed a)147 iLocalDef double f64_Fixed(const iFixed a) {
148     return (double) a.v / iFixedUnit;
149 }
150 
f32_Fixed(const iFixed a)151 iLocalDef float f32_Fixed(const iFixed a) {
152     return (float) f64_Fixed(a);
153 }
154 
min_Fixed(const iFixed a,const iFixed b)155 iLocalDef iFixed min_Fixed(const iFixed a, const iFixed b)  { return init_Fixed(iMin(a.v, b.v)); }
max_Fixed(const iFixed a,const iFixed b)156 iLocalDef iFixed max_Fixed(const iFixed a, const iFixed b)  { return init_Fixed(iMax(a.v, b.v)); }
neg_Fixed(const iFixed a)157 iLocalDef iFixed neg_Fixed(const iFixed a)                  { return init_Fixed(-a.v); }
abs_Fixed(const iFixed a)158 iLocalDef iFixed abs_Fixed(const iFixed a)                  { if (a.comp.sign) return neg_Fixed(a); else return a; }
159 
mix_Fixed(const iFixed a,const iFixed b,const iFixed t)160 iLocalDef iFixed mix_Fixed(const iFixed a, const iFixed b, const iFixed t) {
161     return add_Fixed(a, mul_Fixed(sub_Fixed(b, a), t));
162 }
163 
random_Fixed(void)164 iLocalDef iFixed random_Fixed(void) {
165     return initf_Fixed(iRandomf());
166 }
167