Skip to content

Instantly share code, notes, and snippets.

@Jesserc
Created July 20, 2024 09:11
Show Gist options
  • Save Jesserc/86f12415755c14ca9a062890e60f0d64 to your computer and use it in GitHub Desktop.
Save Jesserc/86f12415755c14ca9a062890e60f0d64 to your computer and use it in GitHub Desktop.

Binary, Bits, Bytes, and Hexadecimal: A Beginner's Guide

1. Binary (Base-2)

Binary is a number system that uses only two digits: 0 and 1. Each digit is called a "bit" (binary digit).

Examples:

0 in binary = 0
1 in binary = 1
2 in binary = 10
3 in binary = 11
4 in binary = 100

How to convert binary to decimal: Each bit represents a power of 2, starting from right to left: 2^0, 2^1, 2^2, 2^3, and so on.

For example, 1011 in binary:

1 0 1 1
│ │ │ │
│ │ │ └─ 1 * 2^0 = 1 * 1 = 1
│ │ └─── 1 * 2^1 = 1 * 2 = 2
│ └───── 0 * 2^2 = 0 * 4 = 0
└─────── 1 * 2^3 = 1 * 8 = 8

Sum: 8 + 0 + 2 + 1 = 11 (decimal)


2. Bits

A bit is the smallest unit of data in computing. It can be either 0 or 1.

- 8 bits = 1 byte
- 4 bits = 1 nibble

The number of possible values for n bits is 2^n:

1 bit  : 2 possible values (0 to 1)
2 bits : 4 possible values (0 to 3)
3 bits : 8 possible values (0 to 7)
4 bits : 16 possible values (0 to 15)
8 bits : 256 possible values (0 to 255)

This is why uint8 in Solidity can hold values from 0 to 255.

3. Bytes

A byte is a unit of digital information that consists of 8 bits.

  • 1 byte can represent 256 different values (2^8 = 256)
  • Bytes are often used to represent characters in computing

ASCII uses 1 byte per character:

'A' = 01000001
'B' = 01000010
'C' = 01000011

4. Hexadecimal (Base-16)

Hexadecimal is a base-16 number system. It uses 16 distinct symbols: 0-9 and A-F.

Decimal : 0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15
Hex     : 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F

Hex is often used because it's more compact than binary and easy to convert to/from binary.

Important relationships:

  • 1 hex digit represents 4 bits (a nibble)
  • 2 hex digits represent 8 bits (a byte)

For example:

  • 0xA is one hex digit, representing 4 bits (1010 in binary)
  • 0xA5 is two hex digits, representing 8 bits (10100101 in binary)

5. Relationship to Solidity Types

uint8: 8-bit unsigned integer

- Range: 0 to 255 (2^8 - 1)
- Binary: 00000000 to 11111111
- Hex: 0x00 to 0xFF

uint16: 16-bit unsigned integer

- Range: 0 to 65535 (2^16 - 1)
- Binary: 0000000000000000 to 1111111111111111
- Hex: 0x0000 to 0xFFFF

bytes1: 1 byte (8 bits)

  • Can store 2 hex characters, any hex value from 0x00 to 0xFF

bytes32: 32 bytes (256 bits)

  • Can store 64 hexadecimal characters
  • Each byte consists of 2 hexadecimal characters. Therefore, a bytes32 value can hold up to 64 hexadecimal characters (excluding the 0x prefix). For example: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef

address: 20 bytes (160 bits)

  • Typically represented as a 40-character hex string, e.g., 0x742d35Cc6634C0532925a3b844Bc454e4438f44e

6. Conversion Tools

For practicing conversions, you can use online tools like:


Remember: Understanding these concepts is crucial for working with low-level data types and optimizing gas usage in Solidity!

// SPDX-License-Identifier: MIT // license of your code, is it free and open-source or not?
pragma solidity ^0.8.0; // specify the solidity compiler version
contract WorkshopExtended {
// Unsigned Integers (uint)
// uints are positive integers only
// uint8, uint16, uint24, ..., uint256 are available (multiples of 8 but size, until 256 bits)
// uint is an alias for uint256
uint8 public smallestUint = 255; // Can hold 0 to 255 (2^8 - 1) // Binary range: 00000000 (0) to 11111111 (255)
// Then you can get other uints by adding 8 to uint8, i.e, uint8+8 = uint16 and then continue, uint24, uint32, and so on to uint256
uint16 public uint16Example = 65535; // Can hold 0 to 65535 (2^16 - 1) // Binary range: 00000000 00000000 (0) to 11111111 11111111 (65,535)
uint24 public uint24Example = 16777215; // Can hold 0 to 16777215 (2^24 - 1) // Binary range: 00000000 00000000 00000000 (0) to 11111111 11111111 11111111 (16,777,215)
uint32 public uint32Example = 4294967295; // Can hold 0 to 4294967295 (2^32 - 1) // Binary range: 00000000 00000000 00000000 00000000 (0) to 11111111 11111111 11111111 11111111 (4,294,967,295)
uint256 public largestUint =
115792089237316195423570985008687907853269984665640564039457584007913129639935; // Can hold max of 256 bits positive integers, 2^256-1
// Signed Integers (int)
// ints can be both positive and negative
// int8, int16, int24, ..., int256 are available
// int is an alias for int256
int8 public smallestInt = -128; // Can hold -128 to 127 (-(2^7) to 2^7 - 1)
int16 public int16Example = -32768; // Can hold -32768 to 32767 (-(2^15) to 2^15 - 1)
int24 public int24Example = 8388607; // Can hold -8388608 to 8388607 (-(2^23) to 2^23 - 1)
int32 public int32Example = -2147483648; // Can hold -2147483648 to 2147483647 (-(2^31) to 2^31 - 1)
int256 public largestInt =
-57896044618658097711785492504343953926634992332820282019728792003956564819968; // Can hold -(2^255) to 2^255 - 1
// Address
// Holds a 20 byte Ethereum address
address public contractOwner = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
// Boolean
// Can hold true or false
bool public isActive = true; // or false
// Bytes
// Fixed-size byte arrays
// bytes1, bytes2, bytes3, ..., bytes32 are available
bytes1 public singleByte = 0x41; // Holds a single byte (8 bits)
bytes32 public data = "Hello, Solidity!"; // Holds 32 bytes
// Dynamic bytes array
bytes public dynamicBytes = "Dynamic bytes array";
// String
// Dynamic UTF-8 encoded string
string public greeting = "Welcome to Solidity Workshop";
// Array
// Dynamic array (can change in size)
uint256[] public dynamicArray;
// Fixed-size array (size cannot change after declaration)
uint256[5] public fixedArray;
// Mapping
// Key-value store
mapping(address => uint256) public balances;
// Enum
// User-defined type with a finite set of constant values
enum Status {
Pending,
Approved,
Rejected
}
Status public currentStatus;
// Struct
// User-defined type that groups together related data
struct Person {
string name;
uint256 age;
address wallet;
}
Person public workshopLead;
// Events
// Used to emit logs on the blockchain
event StatusChanged(Status newStatus);
event EtherReceived(address sender, uint256 amount);
// Constructor
// Called once when the contract is deployed
constructor() {
contractOwner = msg.sender;
currentStatus = Status.Pending;
workshopLead = Person("Jesse", 1000, msg.sender);
}
// Function to initialize dynamic array
function initializeDynamicArray() public {
dynamicArray.push(1);
dynamicArray.push(2);
dynamicArray.push(3);
}
// Function to initialize fixed array
function initializeFixedArray() public {
fixedArray[0] = 10;
fixedArray[1] = 20;
fixedArray[2] = 30;
fixedArray[3] = 40;
fixedArray[4] = 50;
}
// Function to demonstrate enum usage
function changeStatus(Status newStatus) public {
require(msg.sender == contractOwner, "Only owner can change status");
currentStatus = newStatus;
emit StatusChanged(newStatus);
}
// Function to demonstrate struct usage
function updateWorkshopLead(
string memory _name,
uint256 _age,
address _wallet
) public {
require(
msg.sender == contractOwner,
"Only owner can update workshop lead"
);
workshopLead = Person(_name, _age, _wallet);
}
// Pure function (doesn't read or modify state)
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
// View function (reads but doesn't modify state)
function getContractBalance() public view returns (uint256) {
return address(this).balance;
}
// Payable function (can receive Ether)
function deposit() public payable {
balances[msg.sender] += msg.value;
}
// Function demonstrating data location (memory)
function processPersonMemory(Person memory _person)
public
pure
returns (string memory)
{
return _person.name;
}
// Internal function (can only be called within the contract or derived contracts)
function internalFunction() internal pure returns (string memory) {
return "This is an internal function";
}
// Function to demonstrate sending Ether using transfer
function withdrawUsingTransfer(uint256 _amount) public {
require(balances[msg.sender] >= _amount, "Insufficient balance");
balances[msg.sender] -= _amount;
payable(msg.sender).transfer(_amount); // transfer throws an error on failure
}
// Function to demonstrate sending Ether using call
function withdrawUsingCall(uint256 _amount) public {
require(balances[msg.sender] >= _amount, "Insufficient balance");
balances[msg.sender] -= _amount;
(bool sent, ) = payable(msg.sender).call{value: _amount}(""); // call returns a boolean indicating success or failure
require(sent, "Failed to send Ether");
}
// Receive function (called when Ether is sent to the contract without data)
receive() external payable {
emit EtherReceived(msg.sender, msg.value);
}
// Fallback function (called when a function that doesn't exist is called or when Ether is sent with data)
fallback() external payable {
emit EtherReceived(msg.sender, msg.value);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment