9
9
#include < test/fuzz/util.h>
10
10
11
11
#include < compare>
12
+ #include < cmath>
12
13
#include < cstdint>
13
14
#include < iostream>
14
15
@@ -32,6 +33,18 @@ arith_uint256 Abs256(int64_t x)
32
33
}
33
34
}
34
35
36
+ /* * Construct an arith_uint256 whose value equals abs(x), for 96-bit x. */
37
+ arith_uint256 Abs256 (std::pair<int64_t , uint32_t > x)
38
+ {
39
+ if (x.first >= 0 ) {
40
+ // x.first and x.second are both non-negative; sum their absolute values.
41
+ return (Abs256 (x.first ) << 32 ) + Abs256 (x.second );
42
+ } else {
43
+ // x.first is negative and x.second is non-negative; subtract the absolute values.
44
+ return (Abs256 (x.first ) << 32 ) - Abs256 (x.second );
45
+ }
46
+ }
47
+
35
48
std::strong_ordering MulCompare (int64_t a1, int64_t a2, int64_t b1, int64_t b2)
36
49
{
37
50
// Compute and compare signs.
@@ -92,3 +105,98 @@ FUZZ_TARGET(feefrac)
92
105
assert ((fr1 == fr2) == std::is_eq (cmp_total));
93
106
assert ((fr1 != fr2) == std::is_neq (cmp_total));
94
107
}
108
+
109
+ FUZZ_TARGET (feefrac_div_fallback)
110
+ {
111
+ // Verify the behavior of FeeFrac::DivFallback over all possible inputs.
112
+
113
+ // Construct a 96-bit signed value num, and positive 31-bit value den.
114
+ FuzzedDataProvider provider (buffer.data (), buffer.size ());
115
+ auto num_high = provider.ConsumeIntegral <int64_t >();
116
+ auto num_low = provider.ConsumeIntegral <uint32_t >();
117
+ std::pair<int64_t , uint32_t > num{num_high, num_low};
118
+ auto den = provider.ConsumeIntegralInRange <int32_t >(1 , std::numeric_limits<int32_t >::max ());
119
+
120
+ // Predict the sign of the actual result.
121
+ bool is_negative = num_high < 0 ;
122
+ // Evaluate absolute value using arith_uint256. If the actual result is negative, the absolute
123
+ // value of the quotient is the rounded-up quotient of the absolute values.
124
+ auto num_abs = Abs256 (num);
125
+ auto den_abs = Abs256 (den);
126
+ auto quot_abs = is_negative ? (num_abs + den_abs - 1 ) / den_abs : num_abs / den_abs;
127
+
128
+ // If the result is not representable by an int64_t, bail out.
129
+ if ((is_negative && quot_abs > MAX_ABS_INT64) || (!is_negative && quot_abs >= MAX_ABS_INT64)) {
130
+ return ;
131
+ }
132
+
133
+ // Verify the behavior of FeeFrac::DivFallback.
134
+ auto res = FeeFrac::DivFallback (num, den);
135
+ assert ((res < 0 ) == is_negative);
136
+ assert (Abs256 (res) == quot_abs);
137
+
138
+ // Compare approximately with floating-point.
139
+ long double expect = std::floorl (num_high * 4294967296 .0L + num_low) / den;
140
+ // Expect to be accurate within 50 bits of precision, +- 1 sat.
141
+ if (expect == 0 .0L ) {
142
+ assert (res >= -1 && res <= 1 );
143
+ } else if (expect > 0 .0L ) {
144
+ assert (res >= expect * 0 .999999999999999L - 1 .0L );
145
+ assert (res <= expect * 1 .000000000000001L + 1 .0L );
146
+ } else {
147
+ assert (res >= expect * 1 .000000000000001L - 1 .0L );
148
+ assert (res <= expect * 0 .999999999999999L + 1 .0L );
149
+ }
150
+ }
151
+
152
+ FUZZ_TARGET (feefrac_mul_div)
153
+ {
154
+ // Verify the behavior of:
155
+ // - The combination of FeeFrac::Mul + FeeFrac::Div.
156
+ // - The combination of FeeFrac::MulFallback + FeeFrac::DivFallback.
157
+ // - FeeFrac::Evaluate.
158
+
159
+ // Construct a 32-bit signed multiplicand, a 64-bit signed multiplicand, and a positive 31-bit
160
+ // divisor.
161
+ FuzzedDataProvider provider (buffer.data (), buffer.size ());
162
+ auto mul32 = provider.ConsumeIntegral <int32_t >();
163
+ auto mul64 = provider.ConsumeIntegral <int64_t >();
164
+ auto div = provider.ConsumeIntegralInRange <int32_t >(1 , std::numeric_limits<int32_t >::max ());
165
+
166
+ // Predict the sign of the overall result.
167
+ bool is_negative = ((mul32 < 0 ) && (mul64 > 0 )) || ((mul32 > 0 ) && (mul64 < 0 ));
168
+ // Evaluate absolute value using arith_uint256. If the actual result is negative, the absolute
169
+ // value of the quotient is the rounded-up quotient of the absolute values.
170
+ auto prod_abs = Abs256 (mul32) * Abs256 (mul64);
171
+ auto div_abs = Abs256 (div);
172
+ auto quot_abs = is_negative ? (prod_abs + div_abs - 1 ) / div_abs : prod_abs / div_abs;
173
+
174
+ // If the result is not representable by an int64_t, bail out.
175
+ if ((is_negative && quot_abs > MAX_ABS_INT64) || (!is_negative && quot_abs >= MAX_ABS_INT64)) {
176
+ // If 0 <= mul32 <= div, then the result is guaranteed to be representable.
177
+ assert (mul32 < 0 || mul32 > div);
178
+ return ;
179
+ }
180
+
181
+ // Verify the behavior of FeeFrac::Mul + FeeFrac::Div.
182
+ auto res = FeeFrac::Div (FeeFrac::Mul (mul64, mul32), div);
183
+ assert ((res < 0 ) == is_negative);
184
+ assert (Abs256 (res) == quot_abs);
185
+
186
+ // Verify the behavior of FeeFrac::MulFallback + FeeFrac::DivFallback.
187
+ auto res_fallback = FeeFrac::DivFallback (FeeFrac::MulFallback (mul64, mul32), div);
188
+ assert (res == res_fallback);
189
+
190
+ // Compare approximately with floating-point.
191
+ long double expect = std::floorl (static_cast <long double >(mul32) * mul64 / div);
192
+ // Expect to be accurate within 50 bits of precision, +- 1 sat.
193
+ if (expect == 0 .0L ) {
194
+ assert (res >= -1 && res <= 1 );
195
+ } else if (expect > 0 .0L ) {
196
+ assert (res >= expect * 0 .999999999999999L - 1 .0L );
197
+ assert (res <= expect * 1 .000000000000001L + 1 .0L );
198
+ } else {
199
+ assert (res >= expect * 1 .000000000000001L - 1 .0L );
200
+ assert (res <= expect * 0 .999999999999999L + 1 .0L );
201
+ }
202
+ }
0 commit comments