# 基于zkSNARK的LEO编程语言的定点算术 

By [Denny Vozrazhaet](https://paragraph.com/@vozrazhaet) · 2023-03-12

---

![](https://storage.googleapis.com/papyrus_images/ebd7771a5e9ffbd1472332fe59e9de3523c7c31d5e7c234d35472b1535db7d6f.png)

导言

  

基于区块链的系统包含在我们生活的许多领域，如DeFi或游戏等行业。 然而，许多现代区块链缺乏隐私性和可扩展性，这限制了用例的范围。 由aleo等零知识证明支持的零知识证明和块链承诺提供更好的可扩展性和隐私保证，以及启用新的区块链应用程序。 这包括新的DeFi应用程序，更复杂的web3游戏，甚至是基于人工智能的程序。 其中许多应用程序需要表示广泛的数字，包括小数。 Aleo自带Leo编程语言，大大简化了零知识程序的编程。 但是，它仅支持基于整数的数字。 在本文中，我们使用Leo分析了zksnarks中定点数的结构，这使我们也可以使用分数来计算广泛的应用。

  

定点数的简单实现

  

在实现定点数值表示法时，我们可以对变量使用Leo语言提供的整数类型。 此外，我们在内部指定了一个缩放因子，该因子定义了为值的小数点左侧的整数部分保留的数字，并且还定义了值的小数点右侧的小数部分。

  

假设我们想将值1.55表示为小数点后精度为两位数的定点数。 为此，我们可以输入变量i并为其赋值155，即1.55的值乘以100的缩放因子:

  

设i：u32=155;

现在我们可以用这个变量进行数学计算。 例如，要添加0.45，我们添加45（。45\*100)到程序代码，这导致变量200的值。 在解释程序的输出数据时，我们需要将值除以100的缩放因子，以获得所需的十进制数系统-在十进制数系统中加法的结果为2。

  

同样，我们也可以进行乘法运算。 当用十进制表示法乘以2.50时，我们用定点表示法乘以250，然后将结果除以100的缩放因子。 例如，十进制中的2\*2.5=5是指定点表示法中的200\*250/100=500。 再次，当在定点数系统之外解释结果时，我们需要将定点数500除以100的缩放因子，以获得5的预期结果。

  

对于除法，我们的行为类似于乘法，但不是除法，而是乘以缩放因子。

  

例如，十进制中的4.5/0.5=9是指定点表示法中的100\*450/50=900。 除以缩放因子，我们得到9的预期结果。

  

概括和要考虑的事情

  

如上例所示，缩放因子确定小数部分的位数。 我们对n个小数位使用了10^n的缩放因子。 通常，较高的缩放因子提供更高的精度，但是，我们必须记住此类型的可接受值范围。

  

在上述使用u32的例子中，一般范围是从0到232-1=4,294,967,295。 由于类型的二元性质，一般的概念是使用二的幂的比例系数S。 例如，当使用2〇=32的缩放因子时，对于小数部分使用5位，对于整数部分仅保留27位。 因此，整数部分的最大数是22⁷-1=134 217 727，小数部分的分辨率是1/2⁵=1/32。 小数部分可以将31/32添加到最大整数，因此最大可表示值介于0和22⁷-1+31/32=134 217 727.969之间。

  

与此同时，最大表示误差计算为（1/S）/2，因此在本例中它等于（1/2÷）/2=1/64=0.015625。 因此，更大的缩放因子允许我们对小数进行更准确的表示，但减少了整数部分的大小，从而减少了可表示值的范围。

  

正如我们所看到的，我们可以存储的值范围和我们表示的数字的准确性之间存在权衡。

  

特别是在乘法或除法时，我们可能会遇到溢出。 例如，假设我们的缩放因子为2⁵=32，我们希望将21⁶=65536乘以2⁶=64。 下面的代码试图做到这一点。

  

`功能主()->u32{`

`设s：u32=32;`

`令a：u32=65536*s;`

`令b：u32=64*s;`

`设结果为：u32=a*b/s;`

  

返回结果;

}

对于定点表示法，我们必须将两个数字乘以一个缩放因子，实际上将221乘以211，然后再用2‰的缩放因子除以。 通过查看说明，我们期望输出代码为22⁷。

  

然而，当我们看到结果时，我们得到:

  

\[登记册\]

r0：u32=0;

C\*d的临时结果是232，正好在u32类型的范围之外。 所以我们实际上得到了一个数字溢出，得到了一个不正确的结果。

  

那么，我们能做些什么呢？ 我们可以对所有类型（变量和输出）使用类型u64而不是类型u32，这些类型可以存储最多2⁶⁴-1的数字。 因此，我们得到了预期的结果22⁷:

  

\[登记册\]

r0：u64=134217728;

请记住，我们需要再次除以缩放因子，以便用普通术语解释定点数的结果：22⁷/2⁵=222，这是上述计算21⁶乘以2⁶的结果。

  

然而，当使用u64时，电路的尺寸从96个限制增加到192个限制，有效地使尺寸增加了一倍。 这会导致验证成本增加，特别是在复杂的应用中。 因此，另一种选择是减少缩放因子，从而潜在地降低数字表示的准确性。

  

当使用2⁴作为缩放因子而不是仅用于u32类型的2⁵时，我们得到以下结果:

  

\[登记册\]

r0：u32=67108864;

此结果等于22‰。 再次，将其除以2‰的缩放因子，我们得到222的预期和正确结果。

  

该代码通常也适用于负数。 但是，我们需要使用有符号整数类型。 考虑到符号需要一个额外的位，因此对于i32，整数部分的范围计算为+-231-1，其转换为从-2147483648到2147483647的范围。

  

代码示例

  

定点格式的两个数字a和b的加法:

  

功能添加(a:u32,b:u32)->u32{

设结果为：u32=a+b;

返回结果;

}

定点格式的两个数a和b的乘法:

  

乘法函数(a:u32,b:u32,s:u32)->u32{

让结果：u32=a\*b/s;

返回结果;

}

以定点格式划分两个数a和b:

  

功能划分(a:u32,b:u32,s:u32)->u32{

让结果：u32=s\*a/b;

返回结果;

}

---

*Originally published on [Denny Vozrazhaet](https://paragraph.com/@vozrazhaet/zksnark-leo)*
