1. 程式人生 > >Shone.Math開源系列2 — 實數型別(含分數和無理數)的實現

Shone.Math開源系列2 — 實數型別(含分數和無理數)的實現

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