Skip to content

Commit 3347cbd

Browse files
committed
Include small fixes
1 parent 95019eb commit 3347cbd

File tree

1 file changed

+28
-27
lines changed

1 file changed

+28
-27
lines changed

learn_evm/arithmetic-checks.md

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ However, the negative domain is shifted to lie "above" the positive domain.
157157
$$uint256 \text{ domain}$$
158158

159159
$$
160-
├\underset{0}{─}────────────────────────────\underset{\hskip -2em 2^{256} - 1}{─}┤
160+
├\underset{0}{─}────────────────────────────\underset{\hskip -1.5em 2^{256} - 1}{─}┤
161161
$$
162162

163163
```solidity
@@ -168,10 +168,10 @@ $$
168168
$$int256 \text{ domain}$$
169169

170170
$$
171-
\overset{\hskip 1em positive}{
172-
├\underset{0}{─}────────────\underset{\hskip -2em 2^{255} - 1}{─}┤
171+
\overset{positive}{
172+
├\underset{0}{─}────────────\underset{\hskip -1.5em 2^{255} - 1}{─}┤
173173
}
174-
\overset{\hskip 1em negative}{
174+
\overset{negative}{
175175
├────\underset{\hskip -3.5em - 2^{255}}─────────\underset{\hskip -0.4 em -1}{─}┤
176176
}
177177
$$
@@ -184,11 +184,11 @@ $$
184184
```
185185

186186
The maximum positive integer that can be represented in a two's complement system using 256 bits is
187-
`0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff` which is roughly equal to half of the maximum value that can be represented using uint256.
187+
`0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff` which is roughly equal to half of the maximum value that can be represented using `uint256`.
188188
The most significant bit of this number is `0`, while all other bits are `1`.
189189

190190
On the other hand, all negative numbers start with a `1` as their first bit.
191-
If we look at the underlying hex representation of these numbers, they are all greater than or equal to the smallest integer that can be represented using int256, which is `0x8000000000000000000000000000000000000000000000000000000000000000`. The integer's binary representation is a `1` followed by 255 `0`'s.
191+
If we look at the underlying hex representation of these numbers, they are all greater than or equal to the smallest integer that can be represented using `int256`, which is `0x8000000000000000000000000000000000000000000000000000000000000000`. The integer's binary representation is a `1` followed by 255 `0`'s.
192192

193193
To obtain the negative value of an integer in a two's complement system, we flip the underlying bits and add `1`: `-a = ~a + 1`.
194194
An example illustrates this.
@@ -421,7 +421,7 @@ On a 64-bit system, integer addition works in the same way as before.
421421
= 0x0000000000000001 // int64(1)
422422
```
423423

424-
However, when performing the same calculations on a 256-bit machine, we need to extend the sign of the int64 value over all unused bits,
424+
However, when performing the same calculations on a 256-bit machine, we need to extend the sign of the `int64` value over all unused bits,
425425
otherwise the value won't be interpreted correctly.
426426

427427
```solidity
@@ -505,59 +505,59 @@ function overflowInt64(int256 value) public pure returns (bool overflow) {
505505
```
506506

507507
We can simplify the expression to a single comparison if we can shift the disjointed number domain back so that it's connected.
508-
To accomplish this, we subtract the smallest negative int64 `type(int64).min` from a value (or add the underlying unsigned value).
509-
A better way to understand this is by visualizing the signed integer number domain in relation to the unsigned domain (which is demonstrated here using int128).
508+
To accomplish this, we subtract the smallest negative `int64` (`type(int64).min`) from a value (or add the underlying unsigned value).
509+
A better way to understand this is by visualizing the signed integer number domain in relation to the unsigned domain (which is demonstrated here using `int128`).
510510

511511
$$uint256 \text{ domain}$$
512512

513513
$$
514-
├\underset{0}{─}────────────────────────────\underset{\hskip -2em 2^{256} - 1}{─}┤
514+
├\underset{0}{─}────────────────────────────\underset{\hskip -1.5em 2^{256} - 1}{─}┤
515515
$$
516516

517517
$$int256 \text{ domain}$$
518518

519519
$$
520-
\overset{\hskip 1em positive}{
521-
├\underset{0}{─}────────────\underset{\hskip -2em 2^{255} - 1}{─}┤
520+
\overset{positive}{
521+
├\underset{0}{─}────────────\underset{\hskip -1.5em 2^{255} - 1}{─}┤
522522
}
523-
\overset{\hskip 1em negative}{
523+
\overset{negative}{
524524
├────\underset{\hskip -3.5em - 2^{255}}─────────\underset{\hskip -0.4 em -1}{─}┤
525525
}
526526
$$
527527

528-
The domain for uint128/int128 can be visualized as follows.
528+
The domain for `uint128`/`int128` can be visualized as follows.
529529

530530
$$uint128 \text{ domain}$$
531531

532532
$$
533-
├\underset{0}─────────────\underset{\hskip -2em 2^{128}-1}─┤
533+
├\underset{0}─────────────\underset{\hskip -1.5em 2^{128}-1}─┤
534534
\phantom{───────────────}┆
535535
$$
536536

537537
$$int128 \text{ domain}$$
538538

539539
$$
540-
\overset{\hskip 1em positive}{
541-
├\underset{0}{─}────\underset{\hskip -2em 2^{127} - 1}{─}┤
540+
\overset{positive}{
541+
├\underset{0}{─}────\underset{\hskip -1.5em 2^{127} - 1}{─}┤
542542
}
543543
\phantom{────────────────}
544-
\overset{\hskip 1em negative}{
544+
\overset{negative}{
545545
├────\underset{\hskip -3.5em - 2^{127}}─\underset{\hskip -0.4 em -1}{─}┤
546546
}
547547
$$
548548

549549
Note that the scales of the number ranges in the previous section do not accurately depict the magnitude of numbers that are representable with the different types and only serve as a visualization. We can represent twice as many numbers with only one additional bit, yet the uint256 domain has twice the number of bits compared to uint128.
550550

551-
After subtracting `type(int128).min` (or adding $2^{127}$) and essentially shifting the domains to the right, we get the following, connected set of values.
551+
After subtracting `type(int128).min` (or adding `2**127`) and essentially shifting the domains to the right, we get the following, connected set of values.
552552

553553
$$
554-
├\underset{0}─────────────\underset{\hskip -2em 2^{128}-1}─┤
554+
├\underset{0}─────────────\underset{\hskip -1.5em 2^{128}-1}─┤
555555
\phantom{───────────────}┆
556556
$$
557557

558558
$$
559-
\overset{\hskip 1em negative}{├──────┤}
560-
\overset{\hskip 1em positive}{├──────┤}
559+
\overset{negative}{├──────┤}
560+
\overset{positive}{├──────┤}
561561
\phantom{───────────────}┆
562562
$$
563563

@@ -685,7 +685,7 @@ An example might help explain the second case.
685685
```solidity
686686
0xffffffffffffffff800000000000000000000000000000000000000000000000 // type(int192).min
687687
* 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // -1
688-
= 0x0000000000000000800000000000000000000000000000000000000000000000 // type(int192).min (when seen as a int192)
688+
= 0x0000000000000000800000000000000000000000000000000000000000000000 // type(int192).min (when looking at the first 192 bits)
689689
```
690690

691691
A method to address this issue is to always start by sign-extending or cleaning the result before attempting to reconstruct the other multiplicand.
@@ -715,9 +715,10 @@ function checkedMulInt192_2(int192 a, int192 b) public pure returns (int192 c) {
715715
In conclusion, we hope this article has served as an informative guide on signed integer arithmetic within the EVM and the two's complement system.
716716
We have explored:
717717

718-
- the added complexity from handling signed over unsigned integers
719-
- the intricacies involved in managing sub 32-byte types
720-
- the significance of `signextend` and opcodes related to signed integers
721-
- the importance of bit-cleaning
718+
- How the EVM makes use of the two's complement representation
719+
- How integer values are interpreted as signed or unsigned depending on the opcodes used
720+
- The added complexity from handling arithmetic for signed vs. unsigned integers
721+
- The intricacies involved in managing sub 32-byte types
722+
- The importance of bit-cleaning and the significance of `signextend`
722723

723724
While low-level optimizations are attractive, they are also heavily error-prone. This article aims to deepen one's understanding of low-level arithmetic, to reduce these risks. Nevertheless, it is crucial to integrate custom low-level optimizations only after thorough manual analysis, automated testing, and to document any non-obvious assumptions.

0 commit comments

Comments
 (0)