When trying to write another numeric type, I drifted into thinking how to make a bignum type using FixedWidthInteger
. There are master methods to do full addition and multiplication. To do a bignum multiplication, we need to cascade carries while in the multiplication loop. But for now I have to do a full multiplication then a full addition. I remember that computers sometimes have a fused multiply-add instruction, so maybe we should do something similar.
My example code uses extension methods, but the real deal should be new requirements on Numeric
with default implementations given. The names need a lot of bikeshedding:
extension Numeric {
/// Returns the sum of the first operand with the product of the other
/// operands.
///
/// The operation should implement lesser rounding or other precision loss
/// compared to a distinct multiplication then addition steps.
static func fused(addingOf augend: Self, withProductOf multiplicand: Self, and multiplier: Self) -> Self {
return augend + multiplicand * multiplier
}
/// Returns the difference between the first operand and the product of the
/// other operands.
///
/// The operation should implement lesser rounding or other precision loss
/// compared to a distinct multiplication then subtraction steps.
static func fused(subtractionOf minuend: Self, againstProductOf multiplicand: Self, and multiplier: Self) -> Self {
return minuend - multiplicand * multiplier
}
/// Returns the difference between the product of the first two operands and
/// the third operand.
///
/// The operation should implement lesser rounding or other precision loss
/// compared to a distinct multiplication then subtraction steps.
static func fused(takingProductOf multiplicand: Self, and multiplier: Self, subtracting subtrahend: Self) -> Self {
return multiplicand * multiplier - subtrahend
}
/// Increases this value by the product of the given operands.
mutating func addProduct(of multiplicand: Self, and multiplier: Self) {
self += multiplicand * multiplier
}
/// Decreases this value by the product of the given operands.
mutating func subtractProduct(of multiplicand: Self, and multiplier: Self) {
self -= multiplicand * multiplier
}
/// Amplifies this value by the first argument, then increases it by the
/// second.
mutating func multiply(by multiplier: Self, thenAdd addend: Self) {
self *= multiplier
self += addend
}
/// Amplifies this value by the first argument, then decreases it by the
/// second.
mutating func multiply(by multiplier: Self, thenSubtract subtrahend: Self) {
self *= multiplier
self -= subtrahend
}
/// Reset this value to the difference of subtracting the product of this
/// value with the first argument from the second argument.
mutating func subtractProduct(with multiplier: Self, from minuend: Self) {
self = minuend - self * multiplier
}
}
We should probably do similar functions to FixedWidthInteger
:
extension FixedWidthInteger {
/// Returns a tuple containing the high and low parts of the result of
/// multiplying this value by the first given value then adding the second
/// given value to the product.
func multipliedFullWidth(by multiplier: Self, thenAdd addend: Self) -> (high: Self, low: Self.Magnitude) {
// I think this works....
let partialProduct = multipliedFullWidth(by: multiplier)
let newLow: Magnitude, newHigh: Self, overflow: Bool
if addend >= 0 {
let carry: Bool
(newLow, carry) = partialProduct.low.addingReportingOverflow(addend.magnitude)
(newHigh, overflow) = partialProduct.high.addingReportingOverflow(carry ? 1 : 0)
} else {
let borrow: Bool
(newLow, borrow) = partialProduct.low.subtractingReportingOverflow(addend.magnitude)
(newHigh, overflow) = partialProduct.high.subtractingReportingOverflow(borrow ? 1 : 0)
}
assert(!overflow)
return (high: newHigh, low: newLow)
}
/// Returns a tuple containing the sign, high word, and low word of the
/// result of multiplying this value by the first given value then
/// subtracting the second given value to the product.
func multipliedFullWidth(by multiplier: Self, thenSubtract subtrahend: Self) -> (negate: Bool, high: Self.Magnitude, low: Self.Magnitude) {
// Figure out this one yourself.
}
/// Returns a tuple containing the sign, high word, and low word of the
/// result of multiplying this value by the first given value then
/// subtracting that product from the second given value.
func multipliedFullWidth(by multiplier: Self, thenSubtractFrom minuend: Self) -> (negate: Bool, high: Self.Magnitude, low: Self.Magnitude) {
// Figure out this one yourself.
}
}
The purpose of making these protocol requirements is so the default integer types can secretly call assembly level instructions when needed.