Repository: bitcoin/bitcoin
File: src/test/fuzz/feefrac.cpp (C++)

Code before:

namespace {

/** Compute a * b, represented in 4x32 bits, highest limb first. */
std::array<uint32_t, 4> Mul128(uint64_t a, uint64_t b)
{
    std::array<uint32_t, 4> ret{0, 0, 0, 0};

    /** Perform ret += v << (32 * pos), at 128-bit precision. */
    auto add_fn = [&](uint64_t v, int pos) {
        uint64_t accum{0};
        for (int i = 0; i + pos < 4; ++i) {
            // Add current value at limb pos in ret.
            accum += ret[3 - pos - i];
            // Add low or high half of v.
            if (i == 0) accum += v & 0xffffffff;
            if (i == 1) accum += v >> 32;
            // Store lower half of result in limb pos in ret.
            ret[3 - pos - i] = accum & 0xffffffff;
            // Leave carry in accum.
            accum >>= 32;
        }
        // Make sure no overflow.
        assert(accum == 0);
    };

    // Multiply the 4 individual limbs (schoolbook multiply, with base 2^32).
    add_fn((a & 0xffffffff) * (b & 0xffffffff), 0);
    add_fn((a >> 32) * (b & 0xffffffff), 1);
    add_fn((a & 0xffffffff) * (b >> 32), 1);
    add_fn((a >> 32) * (b >> 32), 2);
    return ret;
}

/* comparison helper for std::array */
std::strong_ordering compare_arrays(const std::array<uint32_t, 4>& a, const std::array<uint32_t, 4>& b) {
    for (size_t i = 0; i < a.size(); ++i) {
        if (a[i] != b[i]) return a[i] <=> b[i];
    }
    return std::strong_ordering::equal;
}

std::strong_ordering MulCompare(int64_t a1, int64_t a2, int64_t b1, int64_t b2)
{
    // Compute and compare signs.
    int sign_a = (a1 == 0 ? 0 : a1 < 0 ? -1 : 1) * (a2 == 0 ? 0 : a2 < 0 ? -1 : 1);
    int sign_b = (b1 == 0 ? 0 : b1 < 0 ? -1 : 1) * (b2 == 0 ? 0 : b2 < 0 ? -1 : 1);
    if (sign_a != sign_b) return sign_a <=> sign_b;

    // Compute absolute values.
    uint64_t abs_a1 = static_cast<uint64_t>(a1), abs_a2 = static_cast<uint64_t>(a2);

Review comment (refactor):
Now that Fuzz is always compiled, this should likely be move to `feefrac: add helper functions for 96-bit division` (see https://github.com/bitcoin/bitcoin/actions/runs/13162889382/job/36735875466?pr=30535#step:6:2772)

Code after:
#include <cmath>
#include <cstdint>
#include <iostream>

namespace {

/** The maximum absolute value of an int64_t, as an arith_uint256 (2^63). */
const auto MAX_ABS_INT64 = arith_uint256{1} << 63;

/** Construct an arith_uint256 whose value equals abs(x). */
arith_uint256 Abs256(int64_t x)
{
    if (x >= 0) {
        // For positive numbers, pass through the value.
        return arith_uint256{static_cast<uint64_t>(x)};
    } else if (x > std::numeric_limits<int64_t>::min()) {
        // For negative numbers, negate first.
        return arith_uint256{static_cast<uint64_t>(-x)};
    } else {
        // Special case for x == -2^63 (for which -x results in integer overflow).
        return MAX_ABS_INT64;
    }
}

/** Construct an arith_uint256 whose value equals abs(x), for 96-bit x. */
arith_uint256 Abs256(std::pair<int64_t, uint32_t> x)
{
    if (x.first >= 0) {
        // x.first and x.second are both non-negative; sum their absolute values.
        return (Abs256(x.first) << 32) + Abs256(x.second);
    } else {
        // x.first is negative and x.second is non-negative; subtract the absolute values.
        return (Abs256(x.first) << 32) - Abs256(x.second);
    }
}

std::strong_ordering MulCompare(int64_t a1, int64_t a2, int64_t b1, int64_t b2)
{
    // Compute and compare signs.
    int sign_a = (a1 == 0 ? 0 : a1 < 0 ? -1 : 1) * (a2 == 0 ? 0 : a2 < 0 ? -1 : 1);
    int sign_b = (b1 == 0 ? 0 : b1 < 0 ? -1 : 1) * (b2 == 0 ? 0 : b2 < 0 ? -1 : 1);
    if (sign_a != sign_b) return sign_a <=> sign_b;

    // Compute absolute values of products.
    auto mul_abs_a = Abs256(a1) * Abs256(a2), mul_abs_b = Abs256(b1) * Abs256(b2);

    // Compute products of absolute values.
    if (sign_a < 0) {
        return mul_abs_b <=> mul_abs_a;
    } else {
        return mul_abs_a <=> mul_abs_b;