NEXT社区小课堂 | 第十一课:NEO中数字的表达和运算

NEXT社区线上分享课已经开始了~ 每2周周四分享一次

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1

NEXT社区 | 小课堂

由于近期NEXT社区加入很多新的小伙伴,有在校大学生,有对区块链感兴趣的传统企业从业者。为了更方便、更系统的让NEXT社区的伙伴们了解NEO的技术知识,因此我们开设了小课堂,每周3节,向大家普及NEO相关的知识要点!

vmyGBXgS2u2h4GucKE7mMl4RJq1BksYKZR6dnIlt.png

插播:

NEXT社区线上分享课已经开始了~ 每2周周四分享一次

昨日已结束第一期,NEO社区运营负责人曾程的直播分享:

“区块链游戏到底有哪些可能性?”

想听回播的童鞋们可以关注我们的公众号:NEONEXT

回复“第一期”,即可获取回播方式

每次直播课将会发于公众号进行预告,敬请期待!

NEXT社区小课堂 | 第十一课

NEO中数字的表达和运算

一、计算的问题

1、区块链一般有代币,而代币互相转移必然会设计到计算

2、Neo中有GAS, 精确到小数点后8位,那么如何保障浮点数计算的正确

带着上面两个问题,尝试学习理解一下,一个带有激励机制的网络中,如何保证经济计算的正确。

二、Neo中数据的范围和精确度

1、NEO 的有 1 亿管理代币,NEO 的最小单位为 1,不可再分割。

2、GAS 是燃料代币,最大总量上限为 1 亿,用于实现对 NEO 网络使用时的资源控制。NEO 网络对代币转账和智能合约的运行和存储进行收费,从而实现对记账人的经济激励和防止资源滥用。GAS 的最小单位为 0.00000001。

3、NEO和GAS的具体分发方法可以查看白皮书

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

4、由于GAS是小数,必须保证计算时四舍五入的正确性。

综上所述,NEO系统中数值的范围是:

NEO:0~100000000,精确度为1GAS: 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 规定,浮点数的表示方法为:

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1

浮点数表示方法

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1

浮点数表示举例

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1

整型和浮点型都是64位的情况下,浮点型表示的范围更大,但是虽然范围打,可以表示的数字的数量,确实根据表示数的二进制位确定的。

3、decima型

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1

decimal

从上表可以看出,decimal的有效位数很大,达到了28位,但是表示的数据范围却比float和double类型小。decimal类型并不是C#中的基础类型,所以使用的时候会对计算时的性能有影响。

由于精度高,更适合用来处理金融系统的计算问题。但是其本身计算还是有误差的,精度再高都是实数轴上的离散点。

4、类型的比较

TYPEAPPROXIMATE RANGEPRECISION.NET FRAMEWORK TYPEdouble

±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

TYPERANGESIZE.NET FRAMEWORK TYPEint

-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。

六、如何解决舍入误差

1、

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

2、使用更高精度的类型,具体问题具体分析

七、选择在Neo中处理数据的类型

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1

neon钱包

1、Neo使用long来运算,精度没有问题,范围在普通的加减乘除情况下也很难溢出。我们可以看到1亿相乘也不会超过范围,但是继续相乘就会溢出了,但是不太可能有这种累加的效果,只需要代码中注意就好。

运算大小long.max92233720368547758071亿*1亿10000000000000000

2、Gas的运算需要精确到小数点后8位

我们假定支持最大的数字相乘而不溢出就是满足运算的范围要求,那么整数位需要1016,尾数位需要10-16

Double类型在范围和精度上都满足要求decimal在范围和精度上满足要求

现在虽然已经给Neo选择好类型,但是我们进入代码,会发现Neo使用了一个叫Fixed8的类型表示数据,所以我们进一步讨论。

八、定点数

简单的来说,定点数就是小数点在特定位置的数,比如Neo中的fixed8就是始终保持小数点后8位。

定义一个定点数,需要包含两部分:

1、有多少位

2、小数点在哪里

定点数实际上使用整型表达就可以了,我们只需要另外保存一下精度,比如Fixed8就是把long型除以100000000。下图显示了定点数的格式

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1

定点数如何保存

九、定点数的优缺点

1、由于使用整型保存,所有的计算方法其实和整型类似

2、相同二进制位数下,与整型相比,表示的范围缩小了,因为精度提高了,也可以把整型看成精度为1的定点数。

3、与浮点数相比,定点数运算速度更快。

十、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

扫码关注 NEONEXT 官方公众号

获取更多一手社区资讯

©本文观点仅代表作者本人,绝不代表趣币网赞同其观点或证实其描述。文中部分文字/图片/视频/音频等来源于网络,如侵犯到著作权人的权利,请与我们联系(微信/QQ:1074760229)。本文不作为投资理财建议。转载请注明出处:趣币网

发表评论

登录后才能评论

联系我们

在线咨询:点击这里给我发消息

手机:13798586780

QQ/微信:1074760229

QQ群:551893940

工作时间:工作日9:00-18:00,节假日休息

关注微信