Shone.Math开源系列2 — 实数类型(含分数和无理数)的实现

Shone.Math开源系列2

实数类型(含分数和无理数)的实现

作者:Shone

声明:原创文章欢迎转载,但请注明出处,https://www.cnblogs.com/ShoneSharp。
摘要: 计算机数值计算存在输入进制误差、计算过程的分数和无理数运算误差,是很多编程开发的痛点所在。开源项目Shone.Math提供了统一的实数类型Real,支持分数和无理数计算,做到精度、性能和存储的各方面平衡,可以消除输入进制误差和分数计算误差,大幅减少无理数的计算过程误差。

Shone.Math 是一个支持Math泛型数值计算和Real实数运算(浮点数、分数、PI,E,Log,Exp等无理数)的轻量级基础数学库
。该项目开源地址 https://github.com/shonescript/Shone.Math
,是本人把多年代码积累正式转向.NET 5的第一个开源项目,请大家多多支持了。

本系列博客上个章节详细介绍了Shone.Math的Math的泛型实现,全面支持了系统数值类型。有评论提到了相关数据精度话题,因此今天就把 Shone.Math的特色—“实数运算”
提前进行介绍。

一、数值计算之殇

大家在编程过程中其实不断在跟各种数值类型打交道,为什么没有可以“一统江湖”的数值类型?目前还真没有!
很多动态语言直接使用double(64位二进制浮点数)作为唯一数值,然并卵,立马就会碰到下面经典的翻车案例,不信你打开编辑器测试下面公式:

0.1+0.2 竟然不等于 0.3,而是等于0.30000000000000004

如果使用整数类型int或long,碰到除法 5/2竟然等于2
,那更翻车翻的四脚朝天,因此一般通用计算必须采用浮点数就是这个道理,有误差大家都知道,也就将就将就吧。

二、数值误差难点

在有限的时间和空间约束下,计算机数值计算存在误差主要在于三个方面:

(1)输入时进制表达误差:前面的0.1是十进制小数,用二进制浮点数表示会是个无限循环形式,只能截除尾数导致误差。这是最无奈的,一输入就是不精确。那么使用decimal(128位十进制浮点数)就没有这个问题,但是十进制常规CPU不支持,计算速度慢十几倍,大家也不大愿意用,除了金融系统实在没办法。

(2)计算过程的分数运算误差:除法运算可能产生不可规约分数,用小数点表达就是无限循环形式,必须截除尾数,上面的进制转换误差本质上也是分数导致的问题。该问题采用decimal十进制浮点数也没用,只有使用分数形式才能准确表达,那么需要增加一倍存储和接近四倍计算时间的开销。

(3)计算过程的无理数运算误差:类似PI、E、Sqrt、Log、Exp、Sin、Cos、等运算,都可能产生无理数,用小数表达就是无限不循环的形式,真是不死不休,一辈子也算不完!那些超算中心专门为了计算PI都使用高能计算机,还是算不完,这是用啥进制都没用,用分数也不行。这也是分数表达不受待见的原因,你搞半天,碰到无理数运算,误差立马要算总账,没法控制。
无理数运算随时存在,目前还没有很好的解决方案,多数只能采用更高精度的浮点数如quad(128位二进制浮点数)、甚至bigfloat(可指定任意位数精度的二进制浮点数),但带来存储和计算时间增加的开销也很大。

三、Shone.Math实数解决方案

Shone.Math实数有针对性解决了大部分上述问题,填了很多坑,尽量做到易用性、性能等各方面平衡。各位有兴趣可以到开源项目地址,下载dll试用或代码研究一下,有BUG、问题或建议可以在上面直接提出来,也可以pull参与项目代码完善和实现。

Shone.Math 实数把数值分为几类,并通过类型派生进行表达和计算重载:

(1)可二进制有效表达的浮点数:结果值是有限不循环的二进制整数或小数,可使用1个double数值(如2.5r,与常规数值相同),放在基类Real中进行表达和计算;

(2)分子分母均可二进制有效表达的分数:结果值是无限循环的二进制浮点数,需要采用2个double数值的分数形式(如3\5r,采用反斜杠表示分子分母是一个整体),放在派生类Ration中进行表达和计算;

(3)可间接包含分数系数的各类无理数:结果值是无限不循环的二进制浮点数,但是可以针对常用的部分无理数采用专门的间接表达形式如下,基类为Irration,每个间接无理数都采用2个double数值的分数形式。

a) PI无理数
:IrrationPI,如3\5pi,间接表示3/5*PI的计算值;

b) E无理数
:IrrationE,如3\5e,间接表示3/5*PI的计算值;

c) Sqrt无理数
:IrrationSqrt,如3\5sqt,间接表示sqrt(3/5)的计算值;

d) Sqrd无理数
:IrrationSqrd,如3\5sqd,间接表示(3/5)*(3/5)的计算值;

e) Xp无理数
:IrrationXp,如3\5xp,间接表示pow(10,3/5)的计算值;

f) Exp无理数
:IrrationExp,如3\5exp,间接表示pow(E,3/5)的计算值;

g) Pow无理数
:IrrationPow,如3\5pow,间接表示pow(3,5)的计算值;

h) Log无理数
:IrrationLog,如3\5ln,间接表示底数为E的ln(3/5)计算值;

i) Log10无理数
:IrrationLog10,如3\5lg,间接表示底数为10的lg(3/5)计算值;

j) Logx无理数
:IrrationLog,如3\5log,间接表示底数为3的log(3, 5)计算值;
注意,上述有些间接无理数是互补运算(如sqrd与sqrt、xp与log10、exp与log等),如果两个一起会消解,从而保持计算过程的精确性。

(4)其他无法间接表达的无理数:在计算过程中只要碰到这种类型,比如Sin、Cos三角函数等计算结果还是无理数时只能截断尾数,转化为第一类可二进制有效表达的浮点数,仍是Real表达。

四、Shone.Math实数解决问题

Shone.Math 的Real实数类型设计和实现上尽量做到精度、性能和存储各方面的平衡考虑,在有限的时间和空间内,可以有效减少计算机数值计算误差。

(1)消除了输入时进制表达误差:0.1将被转化为1\10r的分数表示,没有进制转换问题。

(2)消除了计算过程的分数运算误差:支持纯正的分数运算也没有误差。

(3)减少计算过程的无理数运算误差:大量常用的PI、E、Sqrt、Log、Exp等无理数运算采用间接形式表达,直到实在无法间接表达时再截尾处理产生误差,但总体上将大大减少常规计算的总体误差。
当然无理数运算误差不可能彻底解决,Shone.Math只是提出了一个可行的实现方法,具体好坏还有待在实际应用中考证。

五、Shone.Math实数Real使用方法

Shone.Math只有一个dll文件,除了.NET5系统外无任何外部依赖。注意:Shone.Math支持.NETCore3.1/5.0以上版本,一方面是拥抱未来向前看,另一方面是开始时发现.NET4和.NET5差好多内容,如MathF类,Math.Asinh,Acosh,Atanh,还有各种Span,Memory等高级类型,这也符合.NET5一统江湖的趋势。

1 、安装Visual Studio 2019

更新到最新版,在选项设置中打开.net 5 preview支持。

2 、下载nuget包或github代码

Nuget包: https://www.nuget.org/packages/Shone.Math/1.0.7

源代码:https://github.com/shonescript/Shone.Math/releases

3 、引用nuget包或Shone.Math.dll到你的项目中

4 、添加命名空间using Shone;

5 、愉快地使用实数常量、方法

using Shone;   // import Shone namespace
var d = Real.PI*3;     // result is 3pi of IrrationPi
var x = 5.ToReal().Pow(3);     // write in dot style
var ds = new double[]{5, 6, 7}.ToReal().Pow(3);   // calculate array easily

6 、Real也支持作为Math计算

7 、注意:Real只有基类对外暴露方法,其他派生类只能在计算过程中自动产生,比如进行Sqrt()时,如果结果可以间接表达,就会产生如3\5sqrt之类的IrrationSqrt无理数,使用时可以体会一下。

六、Real类型的不足之处

前面也说了,Shone.Math的Real类型解决了输入和过程的分数误差,但没有彻底消除无理数误差,只是进行推迟和消解,要进行等值判断时其精度还只能与double类似。
Real类型设计为class,对大量数值计算而言,会产生堆上内存分配和相关GC,对性能有影响。我最早采用的是struct,但需要1个计算结果、2个double分子分母、还有1个byte的标记,占用存储太大,而且有好多判断,不容易调试。最终经过权衡使用现在的方案。

七、小结

基于.NET 5的开源项目Shone.Math,通过各种精巧实现,提供了统一的泛型数值计算静态类Math和实数类型Real,为开发各类自定义数值、几何、空间、公式解析等泛型和高精度数值应用打下了坚实基础。本系列下一章节将介绍Shone.Math的一些.NET5专用高级特性如ref, Span, Memory的泛型数值计算扩展。
声明:原创文章欢迎转载,但请注明出处,https://www.cnblogs.com/ShoneSharp。

标签:Shone.Math 泛型 数值 计算.NET 5C#