Solidity位操作运算符简介

本文介绍以太坊智能合约开发语言 Solidity 中的位操作运算符,以及如何使用这些 Solidity 位操作符对合约数据执行位操作运算,例如与、或、非、异或等,同时也介绍如何实现 Soldity 不支持的取反、移位等操作。

Solidity位操作运算有助于缩减以太坊交易的成本。本文介绍以太坊智能合约开发语言 Solidity中的位操作运算符,以及如何使用这些Solidity位操作符对合约数据执行位操作运算,例如与、或、非、异或等,同时也介绍如何实现Soldity不支持的取反、移位等操作。 ## 1、Solidity位操作概述 [以太坊](https://learnblockchain.cn/2017/11/20/whatiseth)是一台世界计算机, 可能是最昂贵的那台。由于存储是最消耗gas的操作,因此时不时地需要精打细算, 进行一些位操作,就像汇编开发者在芯片固件编程时所做的一样。这可以让你对数据有更多的控制并最终缩减交易成本。 以太坊智能合约开发语言Solidity支持基本的位操作运算,虽然目前还不支持左/右位移。幸运的是有等价的算数运算。 所有的位操作都是逐位执行的,就和你比较两个不同的数组成员一样,都会按顺序逐位操作。注意:在位操作中0和1,分别对应false和true。 出于简化考虑,我将使用bytes1类型(和byte一样),不过更长的数据类型也是 同样的原理。在下面的例子中我们都是用相同的两个变量a和b: ![](https://img.learnblockchain.cn/2020/02/05_/243393095.png) 在Solidity中,我们使用16进制表示的值来初始化这两个变量: ``` bytes1 a = 0xb5; // [10110101] bytes1 b = 0x56; // [01010110] ``` ## 2、与运算/AND 两个变量中都是1的位,其与计算/AND结果位才是1,否则都是0: ![](https://img.learnblockchain.cn/2020/02/05_/51162289.png) 上图中黄色部分表示计算结果,可以看到只有当两个输入变量a和b的对应位 都是1时,结果的响应位才是1。 在Solidity中,与操作符是`&`: ``` a & b; // 结果: 0x14 [00010100] ``` ## 3、或运算/OR 在计算或操作结果的某一位时,只要任何一个输入变量的对应位是1, 那么结果都是1: ![](https://img.learnblockchain.cn/2020/02/05_/199500939.png) 在Solidity中,或操作符是`|`: ``` a | b; // 结果: 0xf7 [11110111] ``` ## 4、异或运算/XOR 在计算异或运算结果的某一位时,只有当两个输入变量的对应位不一致 时,结果才是1: ![](https://img.learnblockchain.cn/2020/02/05_/257810059.png) 在Solidity中,异或操作符时`^`: ``` a ^ b; // 结果: 0xe3 [11100011] ``` 异或运算有趣的一点是,你把结果和任意一个输入变量再做异或运算, 就可以得到另一个输入变量: ``` 0xe3 ^ a; // 结果: 0x56 == b [01010110] ``` ## 5、非运算/NEG 非运算是单目运算,只需要一个输入变量,它就是取反,原来是1结果 就是0,原来是0结果就是1: ![](https://img.learnblockchain.cn/2020/02/05_/893445540.png) Solidity本身并不支持非运算,不过幸运的是你可以将变量与全1值 异或,就得到同样的结果: ``` a ^ 0xff; // Result: 0x4a [01001010] ``` ## 6、移位运算/SHIFT 位移运算指的是向左或向右移动输入变量的位。 让我们先用十进制数来举个例子。例如对于下面的数值: ``` 00001230 ``` 向左位移3位就得到: ``` 01230000 ``` 换句话说,向左移动3位其实就是将原来的数乘以10的3次方。 同样,如果我们继续向右移动4位,结果就和将输入变量除以10的4次方一样: ``` 00000123 ``` 上面的原理对于二进制移位运算也是适用的,因此当我们向左 移N位时,就等价于乘以2的N次方;向右移M位时,就等价于除以2 的M次方。 由于Solidity目前不支持移位运算,因此我们需要借助于算数运算 来实现同样的效果。 ### 6.1 左移位 ![](https://img.learnblockchain.cn/2020/02/05_/94297501.png) 示例代码如下: ``` var n = 3; var aInt = uint8(a); // Converting bytes1 into 8 bit integer var shifted = aInt * 2 ** n; bytes1(shifted); // Back to bytes. Result: 0xa8 [10101000] ``` ### 6.2 右移位 ![](https://img.learnblockchain.cn/2020/02/05_/565727676.png) 示例代码如下: ``` var n = 2; var aInt = uint8(a); // Converting bytes1 into 8 bit integer var shifted = aInt / 2 ** n; bytes1(shifted); // Back to bytes. Result: 0x2d [00101101] ``` ## 7、提取前N位 我们可以使用与操作来提取变量的前N位,方法就是创建一个掩码变量, 其前N位都是1: ![](https://img.learnblockchain.cn/2020/02/05_/836846654.png) 示例代码如下: ``` var n = 5; var nOnes = bytes1(2 ** n - 1); // Creates 5 1s var mask = shiftLeft(nOnes, 8 - n); // Shift left by 3 positions a & mask; // Result: 0xb0 [10110000] ``` ## 8、提取后N位 利用对2取模计算就可以提取变量的后N位,例如: ``` var n = 5; var lastBits = uint8(a) % 2 ** n; bytes1(lastBits); // Result: 0x15 [00010101] ``` ## 9、数据压缩 有了上面的基础,我们就可以使用更少的存储空间来减少交易成本。 例如,假设有两个变量其实都是只利用了4位,那么我们可以将其 压缩到一个变量里: ![](https://img.learnblockchain.cn/2020/02/05_/435417897.png) 示例代码如下: ``` bytes1 c = 0x0d; bytes1 d = 0x07; var result = shiftLeft(c, 4) | d; // 0xd7 [11010111] ``` ## 完整源代码 下面是本文内容的完整源代码: ``` contract BitsAndPieces { function and(bytes1 a, bytes1 b) returns (bytes1) { return a & b; } function or(bytes1 a, bytes1 b) returns (bytes1) { return a | b; } function xor(bytes1 a, bytes1 b) returns (bytes1) { return a ^ b; } function negate(bytes1 a) returns (bytes1) { return a ^ allOnes(); } function shiftLeft(bytes1 a, uint8 n) returns (bytes1) { var shifted = uint8(a) * 2 ** n; return bytes1(shifted); } function shiftRight(bytes1 a, uint8 n) returns (bytes1) { var shifted = uint8(a) / 2 ** n; return bytes1(shifted); } function getFirstN(bytes1 a, uint8 n) returns (bytes1) { var nOnes = bytes1(2 ** n - 1); var mask = shiftLeft(nOnes, 8 - n); // Total 8 bits return a & mask; } function getLastN(bytes1 a, uint8 n) returns (bytes1) { var lastN = uint8(a) % 2 ** n; return bytes1(lastN); } // Sets all bits to 1 function allOnes() returns (bytes1) { return bytes1(-1); // 0 - 1, since data type is unsigned, this results in all 1s. } // Get bit value at position function getBit(bytes1 a, uint8 n) returns (bool) { return a & shiftLeft(0x01, n) != 0; } // Set bit value at position function setBit(bytes1 a, uint8 n) returns (bytes1) { return a | shiftLeft(0x01, n); } // Set the bit into state "false" function clearBit(bytes1 a, uint8 n) returns (bytes1) { bytes1 mask = negate(shiftLeft(0x01, n)); return a & mask; } } ``` --- 原文链接:[Bitwise Operations and Bit Manipulation in Solidity, Ethereum](https://medium.com/@imolfar/bitwise-operations-and-bit-manipulation-in-solidity-ethereum-1751f3d2e216)

Solidity位操作运算有助于缩减以太坊交易的成本。本文介绍以太坊智能合约开发语言 Solidity中的位操作运算符,以及如何使用这些Solidity位操作符对合约数据执行位操作运算,例如与、或、非、异或等,同时也介绍如何实现Soldity不支持的取反、移位等操作。

1、Solidity位操作概述

以太坊是一台世界计算机, 可能是最昂贵的那台。由于存储是最消耗gas的操作,因此时不时地需要精打细算, 进行一些位操作,就像汇编开发者在芯片固件编程时所做的一样。这可以让你对数据有更多的控制并最终缩减交易成本。

以太坊智能合约开发语言Solidity支持基本的位操作运算,虽然目前还不支持左/右位移。幸运的是有等价的算数运算。

所有的位操作都是逐位执行的,就和你比较两个不同的数组成员一样,都会按顺序逐位操作。注意:在位操作中0和1,分别对应false和true。

出于简化考虑,我将使用bytes1类型(和byte一样),不过更长的数据类型也是 同样的原理。在下面的例子中我们都是用相同的两个变量a和b:

在Solidity中,我们使用16进制表示的值来初始化这两个变量:

bytes1 a = 0xb5; //  [10110101]  
bytes1 b = 0x56; //  [01010110]

2、与运算/AND

两个变量中都是1的位,其与计算/AND结果位才是1,否则都是0:

上图中黄色部分表示计算结果,可以看到只有当两个输入变量a和b的对应位 都是1时,结果的响应位才是1。

在Solidity中,与操作符是&


a & b; // 结果: 0x14  [00010100]  

3、或运算/OR

在计算或操作结果的某一位时,只要任何一个输入变量的对应位是1, 那么结果都是1:

在Solidity中,或操作符是|

a | b; // 结果: 0xf7  [11110111]  

4、异或运算/XOR

在计算异或运算结果的某一位时,只有当两个输入变量的对应位不一致 时,结果才是1:

在Solidity中,异或操作符时^

a ^ b; // 结果: 0xe3  [11100011]  

异或运算有趣的一点是,你把结果和任意一个输入变量再做异或运算, 就可以得到另一个输入变量:

0xe3 ^ a; // 结果: 0x56 == b  [01010110]  

5、非运算/NEG

非运算是单目运算,只需要一个输入变量,它就是取反,原来是1结果 就是0,原来是0结果就是1:

Solidity本身并不支持非运算,不过幸运的是你可以将变量与全1值 异或,就得到同样的结果:


a ^ 0xff; // Result: 0x4a  [01001010]  

6、移位运算/SHIFT

位移运算指的是向左或向右移动输入变量的位。

让我们先用十进制数来举个例子。例如对于下面的数值:

00001230  

向左位移3位就得到:

01230000  

换句话说,向左移动3位其实就是将原来的数乘以10的3次方。

同样,如果我们继续向右移动4位,结果就和将输入变量除以10的4次方一样:

00000123  

上面的原理对于二进制移位运算也是适用的,因此当我们向左 移N位时,就等价于乘以2的N次方;向右移M位时,就等价于除以2 的M次方。

由于Solidity目前不支持移位运算,因此我们需要借助于算数运算 来实现同样的效果。

6.1 左移位

示例代码如下:

var n = 3;   
var aInt = uint8(a); // Converting bytes1 into 8 bit integer  
var shifted = aInt * 2 ** n;  
bytes1(shifted);     // Back to bytes. Result: 0xa8  [10101000]  

6.2 右移位

示例代码如下:

var n = 2;   
var aInt = uint8(a); // Converting bytes1 into 8 bit integer  
var shifted = aInt / 2 ** n;  
bytes1(shifted);     // Back to bytes. Result: 0x2d  [00101101]  

7、提取前N位

我们可以使用与操作来提取变量的前N位,方法就是创建一个掩码变量, 其前N位都是1:

示例代码如下:

var n = 5;  
var nOnes = bytes1(2 ** n - 1); // Creates 5 1s  
var mask = shiftLeft(nOnes, 8 - n); // Shift left by 3 positions  
a & mask; // Result: 0xb0  [10110000]  

8、提取后N位

利用对2取模计算就可以提取变量的后N位,例如:

var n = 5;  
var lastBits = uint8(a) % 2 ** n;  
bytes1(lastBits); // Result: 0x15  [00010101]  

9、数据压缩

有了上面的基础,我们就可以使用更少的存储空间来减少交易成本。 例如,假设有两个变量其实都是只利用了4位,那么我们可以将其 压缩到一个变量里:

示例代码如下:

bytes1 c = 0x0d;  
bytes1 d = 0x07;  
var result = shiftLeft(c, 4) | d; // 0xd7 [11010111]  

完整源代码

下面是本文内容的完整源代码:


contract BitsAndPieces {  
 function and(bytes1 a, bytes1 b) returns (bytes1) {  
 return a & b;  
 }  

 function or(bytes1 a, bytes1 b) returns (bytes1) {  
   return a | b;  
 }  

 function xor(bytes1 a, bytes1 b) returns (bytes1) {  
   return a ^ b;  
 }  

 function negate(bytes1 a) returns (bytes1) {  
 return a ^ allOnes();  
 }  

 function shiftLeft(bytes1 a, uint8 n) returns (bytes1) {  
   var shifted = uint8(a) * 2 ** n;  
   return bytes1(shifted);  
 }  

 function shiftRight(bytes1 a, uint8 n) returns (bytes1) {  
   var shifted = uint8(a) / 2 ** n;  
   return bytes1(shifted);  
 }  

 function getFirstN(bytes1 a, uint8 n) returns (bytes1) {  
   var nOnes = bytes1(2 ** n - 1);  
   var mask = shiftLeft(nOnes, 8 - n); // Total 8 bits  

   return a & mask;  
 }   

 function getLastN(bytes1 a, uint8 n) returns (bytes1) {  
 var lastN = uint8(a) % 2 ** n;  
 return bytes1(lastN);  
 }   

 // Sets all bits to 1  

 function allOnes() returns (bytes1) {  
 return bytes1(-1); // 0 - 1, since data type is unsigned, this results in all 1s.  
 }  

 // Get bit value at position  
 function getBit(bytes1 a, uint8 n) returns (bool) {  
 return a & shiftLeft(0x01, n) != 0;  
 }  

 // Set bit value at position  
 function setBit(bytes1 a, uint8 n) returns (bytes1) {  
 return a | shiftLeft(0x01, n);  
 }  

 // Set the bit into state "false"  
 function clearBit(bytes1 a, uint8 n) returns (bytes1) {  
 bytes1 mask = negate(shiftLeft(0x01, n));  
 return a & mask;  
 }  
}  

原文链接:Bitwise Operations and Bit Manipulation in Solidity, Ethereum

区块链技术网。

  • 发表于 2020-02-04 00:14
  • 阅读 ( 1564 )
  • 学分 ( 20 )
  • 分类:Solidity

评论