CKRFloat - 任意精度计算






4.75/5 (11投票s)
任意精度计算
引言
不幸的是,我在除法函数中发现了一个错误。我已经更新了源代码。
任意精度计算很有趣。我业余时间制作深度缩放的分形电影。我开发了一个自己的多小数类,发现它比互联网上其他类似类(引用自维基百科和其他开发者论坛)要快很多。因此,我想分享我的发现。本文仅简要介绍我的类。如果您想要更详细的信息,请下载代码并进行检查。
我的类应该是一个进一步开发的起点,因为它存在一些可以解决的缺点,通过进一步的努力可能会进一步提高速度。
背景
我之前曾尝试使用decNumber来渲染深度缩放的分形,但是由于无法在运行时控制精度,渲染分形时,深度缩放和浅度缩放所需的时间几乎相同。因此,我不得不开发我自己的多小数类,以便在缩放越深时可以增加精度。
我只实现了四种标准的算术运算。基本上,我将多个数字存储在一个整数数组中,并使用与小学时手工计算相同的方法,只不过是将每个整数视为一个数字来处理。
数组中的每个 64 位整数可以包含 8 位数字,即值在 0 到 100,000,000-1 之间。尽管每个整数中的数字数量很少,但在进行实际的乘法、加法或减法时,无需处理溢出。计算循环完成后,会再次遍历数组并处理溢出。这个第二次循环与乘法的嵌套循环相比微不足道。
这样,乘法(对我来说是最有趣的操作)就实现为一个带有内部 `for` 循环的 `for` 循环。在我测试性能时,我意识到如果内部 `for` 循环中的代码尽可能少,计算速度就会显著提高。即使是简单的比较也很昂贵,必须避免。所以最终,我的实现中的内部循环只有一行代码!
最低有效值首先存储在数组中。我一直在犹豫这是否是一个好的决定。但是,通过在列表末尾追加整数可以轻松地扩展值,而在列表开头插入新整数并偏移整个数组会更耗时。
使用十进制可能有一个缺点,因为我使用整数除法和模运算来处理溢出 - 一个基于 2 的类可以使用按位 AND 和按位移位操作,这两种操作已知速度更快。
现在有了 64 位 Windows,`__int64` 是一个原生变量,这大大加快了计算速度。我可以轻松创建缩放超过数百位小数(即 1e100)的缩放电影。但我想缩放得更深,所以我试图在互联网上寻找其他任意计算模块,我找到了 3 个。第一个是 `apfloat`,与我的类进行比较不公平。它被设计成能够处理数百万甚至数十亿位小数,因此使用堆内存甚至文件存储。另外两个是 `decNumber`,现在已更新以支持 64 位,以及 64 位库 ttmath。我发现当乘以两个 300 位数值时,我的类比 `decNumber` 快 3 倍多,比 `ttmath` 快 7 倍多。加法和减法也比其他两个快近两倍。
除法不是我的强项,我曾认为实现它很麻烦。我需要实现它来计算渲染分形时像素之间的偏移量。但是,除法在渲染分形时只需要几次 - 而其他算术运算需要数百万次甚至数十亿次。
下面的时间是以秒为单位,用于对两个随机生成的 200 位数值进行 10,000 次乘法、100,000 次加法和减法、1,000 次除法。对于其他长度的值也测量了类似的差异。变量赋值未包含在测量中。
该测试在带有 64 位 Windows 的笔记本电脑上进行了多次。由于 Windows 有自己的运行机制,会进行各种后台处理和交换,因此选择了每次测量的最低值 - 在运行几次后,会找到一个不会被超越的值。
库 | 乘法 | 加法 | 减法 | 除法 |
CKRFloat | 0.014 | 0.026 | 0.026 | 0.032 |
decNumber | 0.040 (286%) | 0.048 (185%) | 0.048 (185%) | 0.026 (81%) |
ttmath | 0.084 (600%) | 0.046 (177%) | 0.054 (208%) | 0.028 (88%) |
此外,在将一个值与其自身相乘(即求平方)时可以进行一项优化,这可以将时间减少近 20%。这适用于渲染分形,因此在 `CKRFloat` 中实现了。
Using the Code
CKRFloat
可以以代数方式使用
#include <stdio.h>
#include "CKRFloat.h"
void main()
{
CKRFloat a=23.3, b="18.12321", c=a*b;
printf("%s",c.ToText());
}
历史
- 首次修订
- 添加了源代码
- 修正了一些拼写错误
- 将类型从“技巧/窍门”更改为“文章”,并根据要求添加了关于测量的信息。
- 除法测量不正确;公平的比较应该给出具有相似位数的 D结果,因此当在 CKRFloat 中设置最大精度时,值已更新。
- 除法中的错误修复(2013-04-19)