Solidity位运算:与、或、非、异或、移位

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:

Solidity位运算:与、或、非、异或、移位_第1张图片
上图中黄色部分表示计算结果,可以看到只有当两个输入变量a和b的对应位都是1时,结果的响应位才是1。

在Solidity中,与操作符是&

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

3、或运算/OR

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

Solidity位运算:与、或、非、异或、移位_第2张图片

在Solidity中,或操作符是|

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

4、异或运算/XOR

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

Solidity位运算:与、或、非、异或、移位_第3张图片

在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:

Solidity位运算:与、或、非、异或、移位_第4张图片

示例代码如下:

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位,那么我们可以将其压缩到一个变量里:

Solidity位运算:与、或、非、异或、移位_第5张图片

示例代码如下:

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;
    }
    
}

分享一些比特币、以太坊、EOS、Fabric等区块链相关的交互式在线编程实战教程,汇智网提供:

  • java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
  • php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
  • c#比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在C#代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是C#工程师不可多得的比特币开发学习课程。
  • java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
  • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
  • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。
  • 以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
  • 以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • ERC721以太坊通证实战,课程以一个数字艺术品创作与分享DApp的实战开发为主线,深入讲解以太坊非同质化通证的概念、标准与开发方案。内容包含ERC-721标准的自主实现,讲解OpenZeppelin合约代码库二次开发,实战项目采用Truffle,IPFS,实现了通证以及去中心化的通证交易所。
  • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
  • EOS入门教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
  • 深入浅出玩转EOS钱包开发,本课程以手机EOS钱包的完整开发过程为主线,深入学习EOS区块链应用开发,课程内容即涵盖账户、计算资源、智能合约、动作与交易等EOS区块链的核心概念,同时也讲解如何使用eosjs和eosjs-ecc开发包访问EOS区块链,以及如何在React前端应用中集成对EOS区块链的支持。课程内容深入浅出,非常适合前端工程师深入学习EOS区块链应用开发。
  • Hyperledger Fabric 区块链开发详解,本课程面向初学者,内容即包含Hyperledger Fabric的身份证书与MSP服务、权限策略、信道配置与启动、链码通信接口等核心概念,也包含Fabric网络设计、nodejs链码与应用开发的操作实践,是Nodejs工程师学习Fabric区块链开发的最佳选择。
  • Hyperledger Fabric java 区块链开发详解,课程面向初学者,内容即包含Hyperledger Fabric的身份证书与MSP服务、权限策略、信道配置与启动、链码通信接口等核心概念,也包含Fabric网络设计、java链码与应用开发的操作实践,是java工程师学习Fabric区块链开发的最佳选择。
  • tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。

原文链接:Solidity位操作 - 汇智网

你可能感兴趣的:(以太坊开发)