blob: 98bf061e194621bdc3a2eb65111e35722c7e6dba [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "sw/device/lib/base/math.h"
#include <stddef.h>
uint64_t udiv64_slow(uint64_t a, uint64_t b, uint64_t *rem_out) {
uint64_t quot = 0, rem = 0;
// Schoolbook long division in base 2. See
// https://en.wikipedia.org/wiki/Long_division to recall precisely how this
// works. This algorithm is a bit different from the elementary school base-10
// version, since we can use shifts instead of multiplication.
//
// If `b` is zero, `quot == -1` and `rem == a`. This should not be relied
// upon.
size_t bits = sizeof(uint64_t) * 8;
for (size_t i = 0; i < bits; ++i) {
rem <<= 1;
quot <<= 1;
rem |= (a >> (bits - i - 1)) & 1;
// We need to keep bringing down zeros until `rem`, the running total, is
// large enough that we can subtract off `b`; this tells us the value we
// would have had to multiply `a` by to produce this current step in the
// division.
if (rem >= b) {
rem -= b;
quot |= 1;
}
}
if (rem_out != NULL) {
*rem_out = rem;
}
return quot;
}