NEXT社區小課堂 | 第十一課:NEO中數字的表達和運算

NEXT社區 | 小課堂html

因爲近期NEXT社區加入不少新的小夥伴,有在校大學生,有對區塊鏈感興趣的傳統企業從業者。爲了更方便、更系統的讓NEXT社區的夥伴們瞭解NEO的技術知識,所以咱們開設了小課堂,每週3節,向你們普及NEO相關的知識要點!git

 

NEXT社區小課堂 | 第十一課github

NEO中數字的表達和運算算法

 


 

計算的問題

 

一、區塊鏈通常有代幣,而代幣互相轉移必然會設計到計算數組

 

二、Neo中有GAS, 精確到小數點後8位,那麼如何保障浮點數計算的正確網絡

 

帶着上面兩個問題,嘗試學習理解一下,一個帶有激勵機制的網絡中,如何保證經濟計算的正確。性能

 

 

Neo中數據的範圍和精確度

 

一、NEO 的有 1 億管理代幣,NEO 的最小單位爲 1,不可再分割。學習

 

二、GAS 是燃料代幣,最大總量上限爲 1 億,用於實現對 NEO 網絡使用時的資源控制。NEO 網絡對代幣轉帳和智能合約的運行和存儲進行收費,從而實現對記帳人的經濟激勵和防止資源濫用。GAS 的最小單位爲 0.00000001。區塊鏈

 

三、NEO和GAS的具體分發方法能夠查看白皮書ui

http://docs.neo.org/zh-cn/index.html

 

四、因爲GAS是小數,必須保證計算時四捨五入的正確性。

 

綜上所述,NEO系統中數值的範圍是:


NEO:0~100000000,精確度爲1
GAS: 0.00000001~100000000.00000000

 

 

計算機中數值的表達

 

計算機系統通常來講是對現實世界的模擬,用來運算一個現實中遇到的問題,那麼現實世界和計算機系統通常就有一個映射關係

 

咱們現實世界中的各類數學概念中的數,在計算機中都要有一種表達方式。簡單的描述一下我所認識到的計算機世界:

 

1、整型:

 

整數在C#中是int,long,ulong等類型,咱們看一下他的表示範圍。

 

64位機器上C#類型 範圍
int

Signed:

From −2,147,483,648 

to 2,147,483,647, 

from −(231) 

to 231 − 1

long

Signed:

From −9,223,372,036,854,775,808 

to 9,223,372,036,854,775,807, 

from −(263) 

to 263 − 1

 

在C#中,int的範圍是正負20億左右(十進制),long的範圍是2的63次方=9223372036854775808,若是以米做單位,大約爲光走一千年的距離;若是以秒做單位,大約爲3000億年,十進制是19位的一個數字。

 

因爲Neo最大量是1億,最小單位爲1,在64位機器上作運算基本不會溢出,因此直接用long我以爲也是能夠的。

 

2、浮點型:

 

浮點(英語:floating point,縮寫爲FP)是一種對於實數的近似值數值表現法,由一個有效數字(即尾數)加上冪數來表示,一般是乘以某個基數的整數次指數獲得。

 

以這種表示法表示的數值,稱爲浮點數(floating-point number)。利用浮點進行運算,稱爲浮點計算,這種運算一般伴隨着由於沒法精確表示而進行的近似或舍入。

 

在電腦使用的浮點數被電氣電子工程師協會(IEEE)規範化爲IEEE 754。

 

並非說小數叫作浮點數。準確的來講:「浮點數」是一種表示數字的標準,整數也能夠用浮點數的格式來存儲。

 

在C#的程序語言中,浮點數用float和double來表示。

 

EEE 754 規定,浮點數的表示方法爲:

 

浮點數表示方法

 

浮點數表示舉例

 

 

整型和浮點型都是64位的狀況下,浮點型表示的範圍更大,可是雖然範圍打,能夠表示的數字的數量,確實根據表示數的二進制位肯定的。

 

3、decimal型:

 

decimal

 

從上表能夠看出,decimal的有效位數很大,達到了28位,可是表示的數據範圍卻比float和double類型小。decimal類型並非C#中的基礎類型,因此使用的時候會對計算時的性能有影響。

 

因爲精度高,更適合用來處理金融系統的計算問題。可是其自己計算仍是有偏差的,精度再高都是實數軸上的離散點。

 

4、類型的比較:

 

TYPE APPROXIMATE RANGE PRECISION .NET FRAMEWORK TYPE
double

±5.0 × 10−324

to 

±1.7 × 10308

15-16 

digits

System.Double

float

-3.4 × 1038

to 

+3.4 × 1038

7 digits

System.Single

decimal

(-7.9 x 1028

to 7.9 x 1028)

 / (100 to 1028)

28-29 

significant 

digits

System.Decimal

 

 

TYPE RANGE SIZE .NET FRAMEWORK TYPE
int

-2,147,483,648

to 

2,147,483,647

Signed 

32-bit 

integer

System.

Int32

uint

0

to 

4,294,967,295

Unsigned 

32-bit 

integer

System.

UInt32

long

-9,223,372,036,854,775,808

to 

9,223,372,036,854,775,807

Signed 

64-bit 

integer

System.

Int64

ulong

0

to 

18,446,744,073,709,551,615

Unsigned

64-bit 

integer

System.

UInt64

sbyte -128 to 127

Signed

8-bit 

integer

System.

SByte

short

-32,768

to 

32,767

Signed

16-bit 

integer

System.

Int16


 

 

範圍,精度,準確性

 

爲了更進一步的討論計算機中的數字,咱們須要搞清楚範圍,精度,準確性這三個概念。

 

數的範圍是在數的當前表示格式下,最小的值到最大值中間的間隔。好比16位有符號的整數範圍是-32768 to 32767,超過範圍的就不能表示。

 

須要注意的是,在範圍內的有些數,在當前格式下也不能表示,好比整型就無法表示小數。因此每種類型都只能表達實數軸上離散的點。

 

精度和準確性一般讓人混淆,這是兩個徹底不一樣的概念。精度是「表示數的格式」的一種屬性,1.3333精確到小數點後四位,1.333300精確到小數點後6位,但他們是同一個值。

 

可是1.333300的下一個數是1.333301,1.3333的下一個數是1.3334,可見高精度能夠表示同一個範圍內更多的數。

 

精度會影響到運算結果,因此在系統中須要特別注意這些問題:

Using one digit precision:
0.4 * 0.6 + 0.6 * 0.4

= 0.24 + 0.24 Calculate products
= 0.2 + 0.2 Round to 1 digit
= 0.4 Final result


Using two digit precision:
0.4 * 0.6 + 0.6 * 0.4

= 0.24 + 0.24 Calculate products
= 0.24 + 0.24 Keep the 2 digits
= 0.48 Calculate sum
= 0.5 Round to 1 digit

準確性須要在使用的上下文中討論,表示運算的結果和真實的值之間的差別的大小,準確性和錯誤相關,高準確性意味着小的偏差。

準確性和精度是相關的,可是並非直接相關,低精度在有些場景中,也是準確的,好比:

Byte n0 = 0x03;
Int16 n1 = 0x0003;
Int32 n2 = 0x00000003;
Single n3 = 3.000000f;
Double n4 = 3.000000000000000;

 

每種格式都準確表達了3,可是這些變量的精度是不一樣的,使用的位數也不同,從8到64位。

 

 

舍入偏差

 

假定你要在系統中使用pi,你會發現無論用任何類型的變量,你都沒法準確表達它,你的計算必然包含着偏差,你只能選擇一個近似於pi的數去運算。你使用的不是pi,而是pi+e,假定e爲舍入偏差。

更不爽的是,你的給個運算都會帶來新的偏差,好比你須要計算(a+b),假定a,b和pi同樣是無理數,那麼實際上你計算的是(a + b) + (Ea + Eb + Esum)。

咱們必須找到一種方法減小偏差,使咱們的結果能夠反饋出實際的狀況。

 

能夠看兩個由於計算不許確致使的事故:

·  1990年2月25日,海灣戰爭期間,在沙特阿拉伯宰赫蘭的愛國者導彈防護系統因浮點數舍入錯誤而失效,該系統的計算機精度僅有24位,存在0.0001%的計時偏差,因此有效時間闕值是20個小時。當系統運行100個小時之後,已經積累了0.3422秒的偏差。這個錯誤致使導彈系統不斷地自我循環,而不能正確地瞄準目標。結果未能攔截一枚伊拉克飛毛腿導彈,飛毛腿導彈在軍營中爆炸,形成28名美國陸軍死亡。

·  1996年6月4日,在亞利安五號運載火箭發射後37秒,偏離預約軌道而炸燬。緣由是軟件系統試圖將64位浮點數轉換爲16位浮點數,形成溢出錯誤。

·  溫哥華證券交易所曾開發了一項股票指數。當其在1982年推出時,指數的值是1000.000。在後來的從新計算時屢次運用舍入到小數點後三位的操做。22個月之後,指數的值是524.881,然而事實上應該是1009.811。

 

 

如何解決舍入偏差

一、https://en.wikipedia.org/wiki/Kahan_summation_algorithm

二、使用更高精度的類型,具體問題具體分析

 

 

 

選擇在Neo中處理數據的類型

 

neon錢包

 

一、Neo使用long來運算,精度沒有問題,範圍在普通的加減乘除狀況下也很難溢出。咱們能夠看到1億相乘也不會超過範圍,可是繼續相乘就會溢出了,可是不太可能有這種累加的效果,只須要代碼中注意就好。

 

運算 大小
long.max 9223372036854775807
1億*1億 10000000000000000
 

 

二、Gas的運算須要精確到小數點後8位

 

咱們假定支持最大的數字相乘而不溢出就是知足運算的範圍要求,那麼整數位須要1016,尾數位須要10-16


Double類型在範圍和精度上都知足要求
decimal在範圍和精度上知足要求

 

如今雖然已經給Neo選擇好類型,可是咱們進入代碼,會發現Neo使用了一個叫Fixed8的類型表示數據,因此咱們進一步討論。

 

 

定點數

 

簡單的來講,定點數就是小數點在特定位置的數,好比Neo中的fixed8就是始終保持小數點後8位。

 

定義一個定點數,須要包含兩部分:

 

一、有多少位

二、小數點在哪裏

 

定點數實際上使用整型表達就能夠了,咱們只須要另外保存一下精度,好比Fixed8就是把long型除以100000000。下圖顯示了定點數的格式

 

定點數如何保存

 

 

定點數的優缺點

 

一、因爲使用整型保存,全部的計算方法其實和整型相似

 

二、相同二進制位數下,與整型相比,表示的範圍縮小了,由於精度提升了,也能夠把整型當作精度爲1的定點數。

 

三、與浮點數相比,定點數運算速度更快。

 

 

 

fixed8中的一個bug

 

在看源碼的時候,發現fixed8的乘法是使用位移方法本身實現的,結果發現其準確性並無兩個decimal相乘高,雖然如此小的偏差不必定會給系統帶來什麼影響,可是仍是一個有趣的發現。
https://github.com/neo-project/neo/issues/194

 

 

 

極大的數

 

若是數字的範圍和精度都極大,那麼就須要特殊處理,Neo中使用了BigInteger封裝了一個BigDecimal的結構體。BigInteger二的地方是不支持小數,因此Neo中的小數必須乘以一個精度轉成BigDecimal,BigDecimal只是保存了一下精度。

 

 

 

UInt256, UInt160

 

在Neo中發現還有兩個基於byte的數據類型UInt256,UInt160。這兩個主要用來保存hash算法的的值。

 

UInt256有32的字節大小,256個bit,用來保存"22c199231f06e2091f89e0128e7d875fdfb0fb5fc7c4f916af0c50d04ab70e74"這樣的字符串。這個字符串是64個字符組成的,而64位機器上,char類型是2個字節,爲何用32位就能夠表示呢?咱們看看下面代碼。

public static byte[] HexToBytes(this string value){if (value == null || value.Length == 0)return new byte[0];if (value.Length % 2 == 1)throw new FormatException();byte[] result = new byte[value.Length / 2];for (int i = 0; i < result.Length; i++)result[i] = byte.Parse(value.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier);return result;}

 

 

這段代碼表示把以字符串表示的的16進制的數轉成byte數組。因此只要hash的目標集合在0~f之間就能夠。
 

咱們能夠看到"22c199231f06e2091f89e0128e7d875fdfb0fb5fc7c4f916af0c50d04ab70e74"就在這個範圍,若是你改一下,好比加個z進去,就會拋出異常了。

 

 

 

總結

 

上面只是簡單的介紹,基本上是很容易理解的,可是實際上數的表示和運算是一個很是基礎的問題,越簡單的問題每每包含着很複雜的原理.

 

 

本文來源:https://www.jianshu.com/p/ac337d1869ec

 

 

  聯繫咱們  

微博:https://weibo.com/u/6724929880

官網:https://neonext.club/

QQ羣:612334080

電報:https://t.me/neonextop

twitter:https://twitter.com/NE0NEXT

 

掃碼關注NEO NEXT官方公衆號

獲取更多一手社區資訊

相關文章
相關標籤/搜索