Shone.Math开源系列2

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

作者:Shone

声明:原创文章欢迎转载,但请注明出处,https://www.cnblogs.com/ShoneSharp。

摘要: 计算机数值计算存在输入进制误差、计算过程的分数和无理数运算误差,是很多编程开发的痛点所在。开源项目Shone.Math提供了统一的实数类型Real,支持分数和无理数计算,做到精度、性能和存储的各方面平衡,可以消除输入进制误差和分数计算误差,大幅减少无理数的计算过程误差。

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

本系列博客上个章节详细介绍了Shone.Math的Math<T>的泛型实现,全面支持了系统数值类型。有评论提到了相关数据精度话题,因此今天就把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<T>,Memory<T>等高级类型,这也符合.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<T>计算

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<T>和实数类型Real,为开发各类自定义数值、几何、空间、公式解析等泛型和高精度数值应用打下了坚实基础。本系列下一章节将介绍Shone.Math的一些.NET5专用高级特性如ref, Span, Memory的泛型数值计算扩展。

声明:原创文章欢迎转载,但请注明出处,https://www.cnblogs.com/ShoneSharp。

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

Shone.Math开源系列2 — 实数类型(含分数和无理数)的实现的更多相关文章

  1. Shone.Math开源系列1 — 基于.NET 5实现Math<T>泛型数值计算

    Shone.Math开源系列1 — 基于.NET 5实现Math<T>泛型数值计算 作者:Shone .NET 5 preview 4已经可用了,从微软Build2020给出的信息看,.N ...

  2. 智能合约语言Solidity教程系列2 - 地址类型介绍

    智能合约语言Solidity教程系列第二篇 - Solidity地址类型介绍. 写在前面 Solidity是以太坊智能合约编程语言,阅读本文前,你应该对以太坊.智能合约有所了解,如果你还不了解,建议你 ...

  3. 智能合约语言 Solidity 教程系列3 - 函数类型

    Solidity 教程系列第三篇 - Solidity 函数类型介绍. 写在前面 Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊.智能合约有所了解,如果你还不了解,建议你先看以 ...

  4. mybatis入门系列三之类型转换器

    mybatis入门系列三之类型转换器 类型转换器介绍 mybatis作为一个ORM框架,要求java中的对象与数据库中的表记录应该对应 因此java类名-数据库表名,java类属性名-数据库表字段名, ...

  5. 智能合约语言 Solidity 教程系列2 - 地址类型介绍

    Solidity教程系列第二篇 - Solidity地址类型介绍. 写在前面 Solidity是以太坊智能合约编程语言,阅读本文前,你应该对以太坊.智能合约有所了解,如果你还不了解,建议你先看以太坊是 ...

  6. GitHub C 和 C++ 开源库的清单(含示例代码)

    内容包括:标准库.Web应用框架.人工智能.数据库.图片处理.机器学习.日志.代码分析等. 标准库 C++标准库,包括了STL容器,算法和函数等. C++ Standard Library:是一系列类 ...

  7. javascript系列-class6.String类型

    观察淘宝网商品数据   有一个东西叫服务器>>>>js的作用重要作用之一>>>>交互>>>>人机交互(事件)>>&g ...

  8. MySQL的死锁系列- 锁的类型以及加锁原理

    疫情期间在家工作时,同事使用了 insert into on duplicate key update 语句进行插入去重,但是在测试过程中发现了死锁现象: ERROR 1213 (40001): De ...

  9. springboot源码解析-管中窥豹系列之项目类型(二)

    一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...

随机推荐

  1. 【React踩坑记六】create-react-app创建的react项目通过iP地址访问(实现局域网内访问)

    同项目组的小伙伴想用自己的电脑访问我电脑上开发阶段的create-react-app创建的react项目. 试过了了各种内网穿透工具ngrok以及localtunnel等. 奈何打开效率实在太过于龟速 ...

  2. ACM算法--枚举方法(指数枚举,组合枚举)模板

    // 递归实现指数型枚举 vector<int> chosen; void calc(int x) { if (x == n + 1) { for (int i = 0; i < c ...

  3. POJ - 2387 Til the Cows Come Home (最短路入门)

    Bessie is out in the field and wants to get back to the barn to get as much sleep as possible before ...

  4. 图论--树的直径--DFS+树形DP模板

    #include <iostream> #include <cstring> using namespace std; //maxv:源点能到的最远点,maxdis:最远点对应 ...

  5. postman(动态数据获取)

    一:返回报文为 json 格式 示例:因为充值记录接口中需要用到登录接口返回报文中的信息如下 1.以获取token(JWT)和uid为例 2.在登录接口的tests中写入代码(因为登录接口报文信息中有 ...

  6. QT入门指导

    罗列一些QT学习资料 1. http://www.qter.org/ 包含很多教程,包括著名的<学习之路>系列. 2. http://www.qtcn.org/bbs/index-htm- ...

  7. 消息队列,RabbitMQ、Kafka、RocketMQ

    目录 1.消息列队概述 1.1消息队列MQ 1.2AMQP和JMS 1.2.1AMQP 1.2.2JMS 1.2.3AMOP 与 JMS 区别 1.3消息队列产品 1.3.1 Kafka 1.3.2 ...

  8. dp cf 20190615

    A. Timofey and a tree 这个不算是dp,就是一个思维题,好难想的思维题,看了题解才写出来的, 把点和边分开,如果一条边的两个点颜色不同就是特殊边,特殊边两边连的点就叫特殊点, 如果 ...

  9. maven的安装及环境变量配置

    1.下载maven 2.解压至该路径 3. 新建环境变量MAVEN_HOME , 值为maven包点开路径 环境变量配置: 4. 编辑环境变量Path,追加%MAVEN_HOME%\bin\ 5.一路 ...

  10. 【Hadoop离线基础总结】impala简单介绍及安装部署

    目录 impala的简单介绍 概述 优点 缺点 impala和Hive的关系 impala如何和CDH一起工作 impala的架构及查询计划 impala/hive/spark 对比 impala的安 ...