自定义 C++ 十进制类,如何减少除法结果产生溢出的机会

Custom C++ Decimal class, how to reduce chances of overflow generated by divide result

提问人:intrigued_66 提问时间:12/14/2022 最后编辑:intrigued_66 更新时间:12/14/2022 访问量:70

问:

我已经实现了一个固定的 Decimal 类,但是我遇到了一个溢出问题,这是由于除法时精度的传播引起的。

在较高级别上,小数点由以下形式表示:

template<class MANTISSA>
struct Decimal
{
                        //  Example
                        //  7.14
    MANTISSA mantissa_; //   714
    uint8_t exponent_;  //     2
};

但是,我有一个基本的设计缺陷。我的除法运算符一直在计算余数,直到它为零或我们用完了数字。后一种情况发生的频率比我想象的要高。

然后,我将除法的输出乘以另一个小数点:

Decimal operator*(const Decimal& rhs) const
{
    Decimal result = *this;
    result.mantissa_ *= rhs.mantissa_;
    result.exponent_ += rhs.exponent_;
    return result;
}

这里很可能会溢出,因为来自除法输出的尾数已经有最大位数(18 表示 )。int64_t

为了解决这个问题,我认为我可以检测到溢出并减少乘数。我更改了以检查指数总和是否超过 18。但是,这是一个错误。指数可能小于 18,但尾数仍然足够大以溢出。operator*()

为了正确解决这个问题,我需要在乘法之前计算尾数位数。这需要一个 while 循环,将尾数除以 10,直到你达到零,然后对两个乘数都这样做 - 非常昂贵!这肯定不是最好的方法??

在这一点上,我退后了一步,因为事情看起来很复杂,我应该如何处理这个问题?我是否从源头上解决问题,不要让除法导致这么多数字?假设是这样,我是否要求用户配置应该输出多少位数的除数?除法输出的精度是否应该取决于两个输入参数的大小/指数?

谁能提供建议?

C++ 数学 x86 浮点 十进制

评论

2赞 Peter Cordes 12/14/2022
乘法运算符已损坏;对于归一化数字,您需要取加宽乘法的高半部分并重新归一化(这可能涉及 1 的平移,或者十进制浮点格式的 10 缩放)。假设是普通整数类型,则取乘积的下半部分。mantissa_
2赞 chux - Reinstate Monica 12/14/2022
“我的除法运算符一直在计算余数,直到它为零或我们用完了数字>。”
1赞 Peter Cordes 12/14/2022
IDK,我还没有看过十进制浮点数存在哪些库。如果您的目标是学习 FP 数学,那么这听起来像是一个有趣的爱好项目。如果要获得高性能和可用性,我希望现有库对于最常见的宽度相当不错,例如 128 位总大小(IEEE decimal128 - en.wikipedia.org/wiki/Decimal128_floating-point_format)
1赞 Peter Cordes 12/14/2022
你真的坚持 128 位尾数,而不是 128 位尾数和 110 位尾数吗?128 位尾数意味着在具有 17 字节数据的结构中浪费大量空间进行对齐。Wiki 文章讨论了二进制尾数与 BCD 或类似的基于十进制的格式的实现选择。如果没有硬件支持,二进制尾数会使某些操作速度更快,但可能会使除法非常昂贵;我还没有确切地研究 FP 除法是如何工作的,以获得带有小数位的尾数宽度商,而不是从整数除法中获得的余数。
1赞 Peter Cordes 12/14/2022
我是说指数和符号位可以是 128 位整数中的字段。你通过掩盖它们来获得尾数。尾数的实际格式取决于你;它可以是二进制整数、BCD 或 DPD,或者其他一些东西。

答: 暂无答案