提问人:ZeunO8 提问时间:4/26/2023 最后编辑:greybeardZeunO8 更新时间:5/18/2023 访问量:414
可变位浮点数的乘法和除法
Multiply and divide of variable bit floating point numbers
问:
我正在开发一个允许创建大数(整数和浮点数)的库。您可以在此处找到公共存储库BigNumber
我已经为 和 实现了加法和减法,但是只为BigFloating
BigInteger
BigInteger
浮点数的位存储在 a 中,格式如下:std::vector
sign (1 bit)|binary integer(variable bits)|binary fraction(variable bits)
这样这个数字就会有位5.5
0 101 1
用这种格式对数字进行乘除有哪些算法?
即
(5.5) 101 1
* (5.5) 101 1
-------------
= (30.25) 11110 01
或
(5.5) 101 1
/ (5.5) 101 1
-------------
= (1) 1
要实现的功能位于:BigCommon.cpp
std::tuple<std::vector<bool>, size_t, size_t> BigCommon::multiplyBits(const std::vector<bool> &lhsBits, const std::vector<bool> &rhsBits, const size_t &integerBitsSize, const size_t &mantissaBitsSize)
和
std::tuple<std::vector<bool>, size_t, size_t> BigCommon::divideBits(const std::vector<bool> &lhsBits, const std::vector<bool> &rhsBits, const size_t &integerBitsSize, const size_t &mantissaBitsSize)
更新
我实现了multiplyBits算法,如下所示:
std::tuple<std::vector<bool>, size_t, size_t> BigCommon::multiplyBits(const std::vector<bool> &lhsBits, const std::vector<bool> &rhsBits, const size_t &integerBitsSize, const size_t &mantissaBitsSize)
{
std::vector<bool> result;
result.insert(result.begin(), lhsBits[0] ^ rhsBits[0]);
size_t newIntegerBitsSize = 0;
size_t newMantissaBitsSize = mantissaBitsSize + mantissaBitsSize;
std::vector<bool> lhsBinary(lhsBits.begin() + 1, lhsBits.end());
std::vector<bool> rhsBinary(rhsBits.begin() + 1, rhsBits.end());
std::vector<bool> multResult = multiplyBinaryVectors(lhsBinary, rhsBinary);
newIntegerBitsSize = multResult.size() - newMantissaBitsSize;
result.insert(result.begin() + 1, multResult.begin(), multResult.end());
return {result, newIntegerBitsSize, newMantissaBitsSize};
};
现在只是为了分裂!
更新 2
我使用以下算法成功实现了除法:
代码被编辑以支持答案。
更新 3
经过一些测试,我发现除法算法不适用于某些类型的数字,以下是一些测试用例: .绝对与5 / 0.27
10 / 100
divideBinaryVectors
答:
您描述的是一个二进制定点表示形式,其大小在固定二进制点的上方和下方都是可变的(无界的)。
对于这种表示形式,乘法非常简单——只需将每个数字的整数位和小数位连接起来,乘以,然后再次拆分为整数/分数。如果输入数字分别具有 j 和 k 小数位,则结果将具有 j+k 小数位。
除法更难,因为当结果不能完全以二进制表示时,您需要决定继续除法的程度(多少额外的小数位)来计算。从根本上说,它只是长除法——将整数/分数位连接起来,除法(一个可能不会终止的过程),然后分割结果。例如,10/5.5 给你
1.11010 00101 11010 ....
/----------------------
101.1 / 1010.00000 00000 00000
101 1
100 10000 00000 00000
10 11
1 11000 00000 00000
1 011
1100 00000 00000
1011
1 00000 00000
1011
1010 00000
101 1
100 10000
10 11
1 11000
1 011
1100
1011
1
在结果中重复 10 位值。您需要决定何时将其切断并四舍五入分数。
评论
std::vector<bool>
我对使用 std::vector<bool>
表示符号和有效位以及size_t
表示整数位对大数进行乘除的算法有哪些?
评估速度快于速度慢的最佳建议:
转换为有用的格式,计算并转换回来。
对于计算“in”这种格式:
乘法相对不受数字表示差异的影响。
要知道的算法是长乘法和 Toom,即 Toom-2/Karatsuba。
不执行/预检查除法(通常与恢复除法混为一谈)并不比不恢复更费力,因为比较允许提前出局:对于每个商位,平均需要检查大约三个位。
除了缺少规范之外,我发现截至 2023/05/04 的 BigCommon.cpp 实现尝试存在两个问题:
- 基于参数终止计算的方法非常可疑
mantissaBitsSize
- 假设做正确的事情,使用减法。
无论迭代方向如何,仅计算余数和除数位是没有意义的。remainder >= divisor
评论
borrow = !left && (borrow || right) || right && borrow
Boost.Multiprecision 库中的 cpp_bin_float
类可根据需要使用二进制表示形式实现任意大的浮点运算。它还支持无穷大和 NaN 的特殊边缘情况。
除法实现在这里。这有点复杂,但你可以在调试器下运行一个简单的示例代码来理解逻辑。基本上,正如这里已经说过的,对于除法,您只需将数字假定为整数,然后计算分隔小数部分的点的位置。但首先,您必须决定要获得的目标精度。Boost 的实现还在后台使用任意长整数算术。
关于实现的注意事项:
强烈建议避免使用 .更好的选择可能是 boost::d ynamic_bitset。std::vector<bool>
为了对定点二进制数进行除法,我们可以使用以下算法,这是长除法的修改版本,可以正确更新 integerBitsSize 和 fractionBitsSize
std::tuple<std::vector<bool>, size_t, size_t> BigCommon::divideBits(const std::vector<bool> &lhsBits, const std::vector<bool> &rhsBits, const size_t &integerBitsSize, const size_t &fractionBitsSize)
{
if (isZero(rhsBits))
{
throw std::domain_error("Division by zero");
}
bool resultSign = lhsBits.front() ^ rhsBits.front();
std::vector<bool> lhs(lhsBits.begin() + 1, lhsBits.end());
std::vector<bool> rhs(rhsBits.begin() + 1, rhsBits.end());
// Normalize dividend and divisor to remove leading zeros
normalizeBits(lhs);
normalizeBits(rhs);
std::vector<bool> dividend = lhs;
std::vector<bool> divisor = rhs;
std::vector<bool> result;
std::vector<bool> tempDividend;
size_t resultIntegerBitsSize = 0;
size_t resultFractionBitsSize = 0;
size_t dividendSize = dividend.size();
size_t dividendIndex = 0;
while (true)
{
tempDividend.insert(tempDividend.end(), dividendIndex < dividendSize ? dividend[dividendIndex] : false);
dividendIndex++;
if (binaryVectorLessThanOrEqualsTo(divisor, tempDividend))
{
result.insert(result.end(), true);
tempDividend = subtractBinaryVectors(tempDividend, divisor);
}
else
{
result.insert(result.end(), false);
}
if (dividendIndex <= dividendSize)
{
resultIntegerBitsSize++;
}
else
{
resultFractionBitsSize++;
if (resultFractionBitsSize >= 128)
{
break;
}
}
}
while (resultIntegerBitsSize > 0 && !result.front())
{
result.erase(result.begin());
resultIntegerBitsSize--;
}
while (resultFractionBitsSize > 0 && !result.back())
{
result.erase(result.end() - 1);
resultFractionBitsSize--;
}
result.insert(result.begin(), resultSign);
return {result, resultIntegerBitsSize, resultFractionBitsSize};
};
它已经在失败的测试用例上进行了测试,并返回了正确的结果。5 / 0.27
10 / 100
上一个:复数显示为非常小的小数
下一个:浮点数学坏了吗?
评论
0 101 1
0 101 101
0
对于符号位(正),对于 5,将 .5 表示为二进制分数101
1
.5
1
.1b
1b / 10b
0.5d
5d / 10d