<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[z0rr0's blog]]></title><description><![CDATA[Backend developer, tech leader.]]></description><link>https://z0rr0.blog</link><generator>RSS for Node</generator><lastBuildDate>Mon, 12 Oct 2026 15:41:51 GMT</lastBuildDate><atom:link href="https://z0rr0.blog/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Unsigned Integers in Programming]]></title><description><![CDATA[Disclaimer: this post is an English translation of my original article, first published at z0rr0.blog/unsigned-integers.
Why Do We Need Them?
This is a short discussion about unsigned integers in programming. From a mathematical point of view, they m...]]></description><link>https://z0rr0.blog/unsigned-integers-en</link><guid isPermaLink="true">https://z0rr0.blog/unsigned-integers-en</guid><category><![CDATA[Go Language]]></category><dc:creator><![CDATA[Aleksand Zaitsev]]></dc:creator><pubDate>Sat, 10 Jan 2026 06:01:13 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768137161068/fc0e9193-a4ee-496c-bc13-3f5cfedaa5a7.jpeg" alt class="image--center mx-auto" /></p>
<p><strong>Disclaimer</strong>: this post is an English translation of my original article, first published at <a target="_blank" href="https://z0rr0.blog/unsigned-integers">z0rr0.blog/unsigned-integers</a>.</p>
<h2 id="heading-why-do-we-need-them">Why Do We Need Them?</h2>
<p>This is a short discussion about unsigned integers in programming. From a mathematical point of view, they may look a bit strange - why do we even need such “truncated” numbers?</p>
<p>Nevertheless, there are solid reasons for their existence. Here are the main ones:</p>
<ol>
<li><p><strong>Extending the available numeric range using the same number of bits.</strong> You can store more values if negative numbers are not required. For example, an 8-bit signed integer can store values from −128 to 127, while an unsigned type can store values from 0 to 255.</p>
</li>
<li><p><strong>Additional control over business logic.</strong> Sometimes application requirements explicitly forbid negative values (for example, quantity of goods, customer age, file size, etc.). Using unsigned types helps to clearly express these constraints and prevents errors related to improper use of negative numbers.</p>
</li>
<li><p><strong>Performance optimizations.</strong> On some CPU architectures, operations on unsigned numbers may be faster than on signed ones. Additionally, this serves as a hint for the compiler or interpreter that certain optimizations are possible.</p>
</li>
</ol>
<h2 id="heading-how-do-they-work">How Do They Work?</h2>
<p>Let’s write a few small examples to clearly see how unsigned integers behave.</p>
<p>Here is an example in Go. The program iterates over values from 0 to 259 and prints their representation as signed and unsigned numbers, along with their binary and hexadecimal forms.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"unsafe"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">s2u</span><span class="hljs-params">(s <span class="hljs-keyword">int8</span>)</span> <span class="hljs-title">uint8</span></span> {
    <span class="hljs-keyword">return</span> *(*<span class="hljs-keyword">uint8</span>)(unsafe.Pointer(&amp;s))
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> (
        signed   <span class="hljs-keyword">int8</span>
        unsigned <span class="hljs-keyword">uint8</span>
    )

    size := unsafe.Sizeof(signed)
    fmt.Printf(<span class="hljs-string">"Size of int8 and uint8: %d byte(s)\n\n"</span>, size)

    <span class="hljs-built_in">println</span>(<span class="hljs-string">"+---------------------------------------------------------------------------------+"</span>)
    <span class="hljs-built_in">println</span>(<span class="hljs-string">"|                    Compare signed and unsigned numbers                          |"</span>)
    <span class="hljs-built_in">println</span>(<span class="hljs-string">"+-----+----------+----------+-----+--------+-----------+-----+--------------------+"</span>)
    <span class="hljs-built_in">println</span>(<span class="hljs-string">"| #   | unsigned |   bin    | hex | signed |    bin    | hex | signed as unsigned |"</span>)
    <span class="hljs-built_in">println</span>(<span class="hljs-string">"|-----|----------|----------|-----|--------|-----------|-----|--------------------|"</span>)

    <span class="hljs-keyword">for</span> i := <span class="hljs-keyword">range</span> <span class="hljs-number">260</span> {
        signed = <span class="hljs-keyword">int8</span>(i)
        unsigned = <span class="hljs-keyword">uint8</span>(i)
        signedAsUnsigned := s2u(signed)

        fmt.Printf(<span class="hljs-string">"| %-3[1]d | %-8[2]d | %-8[2]b | %-3[2]x | %-6[3]d | %-9[3]b | %-3[3]x | %-18[4]b |\n"</span>, i, unsigned, signed, signedAsUnsigned)
    }

    <span class="hljs-built_in">println</span>(<span class="hljs-string">"+-----+----------+----------+-----+--------+-----------+-----+--------------------+"</span>)
}
</code></pre>
<p>The output looks like this (heavily truncated for readability, with <code>...</code> inserted):</p>
<pre><code class="lang-plaintext">Size of int8 and uint8: 1 byte(s)

+---------------------------------------------------------------------------------+
|                    Compare signed and unsigned numbers                          |
+-----+----------+----------+-----+--------+-----------+-----+--------------------+
| #   | unsigned |   bin    | hex | signed |    bin    | hex | signed as unsigned |
|-----|----------|----------|-----|--------|-----------|-----|--------------------|
| 0   | 0        | 0        | 0   | 0      | 0         | 0   | 0                  |
| 1   | 1        | 1        | 1   | 1      | 1         | 1   | 1                  |
| 2   | 2        | 10       | 2   | 2      | 10        | 2   | 10                 |
...
| 126 | 126      | 1111110  | 7e  | 126    | 1111110   | 7e  | 1111110            |
| 127 | 127      | 1111111  | 7f  | 127    | 1111111   | 7f  | 1111111            |
| 128 | 128      | 10000000 | 80  | -128   | -10000000 | -80 | 10000000           |
| 129 | 129      | 10000001 | 81  | -127   | -1111111  | -7f | 10000001           |
| 130 | 130      | 10000010 | 82  | -126   | -1111110  | -7e | 10000010           |
...
| 254 | 254      | 11111110 | fe  | -2     | -10       | -2  | 11111110           |
| 255 | 255      | 11111111 | ff  | -1     | -1        | -1  | 11111111           |
| 256 | 0        | 0        | 0   | 0      | 0         | 0   | 0                  |
| 257 | 1        | 1        | 1   | 1      | 1         | 1   | 1                  |
| 258 | 2        | 10       | 2   | 2      | 10        | 2   | 10                 |
| 259 | 3        | 11       | 3   | 3      | 11        | 3   | 11                 |
+-----+----------+----------+-----+--------+-----------+-----+--------------------+
</code></pre>
<p>And here is a similar example in Rust:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> size = std::mem::size_of::&lt;<span class="hljs-built_in">u8</span>&gt;();
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Size of i8 and u8: {} byte(s)\n"</span>, size);

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"+---------------------------------------------------------------------------------+"</span>);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"|                    Compare signed and unsigned numbers                          |"</span>);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"+-----+----------+----------+-----+--------+-----------+-----+--------------------+"</span>);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"| #   | unsigned |   bin    | hex | signed |    bin    | hex | signed as unsigned |"</span>);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"|-----|----------|----------|-----|--------|-----------|-----|--------------------|"</span>);

    <span class="hljs-keyword">for</span> number <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>..<span class="hljs-number">260</span> {
        <span class="hljs-comment">// cast 32 bit integer to 8 bit one</span>
        <span class="hljs-keyword">let</span> unsigned: <span class="hljs-built_in">u8</span> = number <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>; <span class="hljs-comment">// unsigned integer 8 bit</span>
        <span class="hljs-keyword">let</span> signed: <span class="hljs-built_in">i8</span> = unsigned <span class="hljs-keyword">as</span> <span class="hljs-built_in">i8</span>; <span class="hljs-comment">// signed integer 8 bit</span>
        <span class="hljs-keyword">let</span> s2u: <span class="hljs-built_in">u8</span> = signed <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>; <span class="hljs-comment">// cast signed to unsigned</span>

        <span class="hljs-built_in">println!</span>(
            <span class="hljs-string">"| {0:&lt;3} | {1:&lt;8} | {1:&lt;8b} | {1:&lt;3x} | {2:&lt;6} | {2:&lt;9b} | {2:&lt;3x} | {3:&lt;18b} |"</span>,
            number, unsigned, signed, s2u,
        );
    }

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"+-----+----------+----------+-----+--------+-----------+-----+--------------------+"</span>);
}
</code></pre>
<p>The output is very similar, but with a few differences:</p>
<ul>
<li><p>In Rust, negative numbers are printed without a minus sign in binary and hexadecimal representations.</p>
</li>
<li><p>In Go, the same values are printed using two’s complement with a leading minus sign.</p>
</li>
</ul>
<p>It is important to understand that this is only a difference in representation. In memory, both languages store exactly the same data, and Rust also uses two’s complement internally.</p>
<pre><code class="lang-plaintext">Size of i8 and u8: 1 byte(s)

+---------------------------------------------------------------------------------+
|                    Compare signed and unsigned numbers                          |
+-----+----------+----------+-----+--------+-----------+-----+--------------------+
| #   | unsigned |   bin    | hex | signed |    bin    | hex | signed as unsigned |
|-----|----------|----------|-----|--------|-----------|-----|--------------------|
| 0   | 0        | 0        | 0   | 0      | 0         | 0   | 0                  |
| 1   | 1        | 1        | 1   | 1      | 1         | 1   | 1                  |
| 2   | 2        | 10       | 2   | 2      | 10        | 2   | 10                 |
...
| 126 | 126      | 1111110  | 7e  | 126    | 1111110   | 7e  | 1111110            |
| 127 | 127      | 1111111  | 7f  | 127    | 1111111   | 7f  | 1111111            |
| 128 | 128      | 10000000 | 80  | -128   | 10000000  | 80  | 10000000           |
| 129 | 129      | 10000001 | 81  | -127   | 10000001  | 81  | 10000001           |
| 130 | 130      | 10000010 | 82  | -126   | 10000010  | 82  | 10000010           |
...
| 254 | 254      | 11111110 | fe  | -2     | 11111110  | fe  | 11111110           |
| 255 | 255      | 11111111 | ff  | -1     | 11111111  | ff  | 11111111           |
| 256 | 0        | 0        | 0   | 0      | 0         | 0   | 0                  |
| 257 | 1        | 1        | 1   | 1      | 1         | 1   | 1                  |
| 258 | 2        | 10       | 2   | 2      | 10        | 2   | 10                 |
| 259 | 3        | 11       | 3   | 3      | 11        | 3   | 11                 |
+-----+----------+----------+-----+--------+-----------+-----+--------------------+
</code></pre>
<p><strong>A Short Digression on Two’s Complement.</strong> In a very simplified form, the algorithm for obtaining two’s complement (using <code>-2</code> as an example) can be described as follows:</p>
<ul>
<li><p>As shown in the Rust output, the binary representation of <code>-2</code> is <code>11111110</code>.</p>
</li>
<li><p>The most significant bit (the leftmost one) represents the sign: <code>0</code> for positive, <code>1</code> for negative.</p>
</li>
<li><p>If the most significant bit is <code>0</code>, the two’s complement representation is identical to the direct binary representation.</p>
</li>
<li><p>Otherwise, to obtain the two’s complement of a negative number, invert all bits except the most significant one, resulting in <code>10000001</code>, and then add <code>1</code>, producing <code>10000010</code>. The first bit is interpreted as the sign, and the remaining bits (<code>0000010</code>) represent the value <code>2</code>. The final result is <code>-2</code>.</p>
</li>
</ul>
<p>Looking at the outputs above, two natural questions arise:</p>
<ol>
<li><p>How can the same binary representation be interpreted differently by the computer?</p>
</li>
<li><p>How do we correctly convert signed values to unsigned ones and vice versa?</p>
</li>
</ol>
<p>For the first question, the answer is simple: there is no difference in memory. During compilation or interpretation, the analyzer tracks which type is used at each point. Depending on that type, the same sequence of bits is interpreted differently. Type information effectively lives in the program’s “metadata”, not in the data itself.</p>
<p>Consider this Go program <code>program.go</code>:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">signedLess</span><span class="hljs-params">(a, b <span class="hljs-keyword">int8</span>)</span> <span class="hljs-title">bool</span></span> {
    <span class="hljs-keyword">return</span> a &lt; b
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">unsignedLess</span><span class="hljs-params">(a, b <span class="hljs-keyword">uint8</span>)</span> <span class="hljs-title">bool</span></span> {
    <span class="hljs-keyword">return</span> a &lt; b
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> (
        signedA   <span class="hljs-keyword">int8</span>  = <span class="hljs-number">-1</span>
        signedB   <span class="hljs-keyword">int8</span>  = <span class="hljs-number">2</span>
        unsignedA <span class="hljs-keyword">uint8</span> = <span class="hljs-number">255</span>
        unsignedB <span class="hljs-keyword">uint8</span> = <span class="hljs-number">2</span>
    )

    <span class="hljs-built_in">println</span>(signedLess(signedA, signedB))
    <span class="hljs-built_in">println</span>(unsignedLess(unsignedA, unsignedB))
}
</code></pre>
<p>If we inspect its ARM64 assembly output:</p>
<pre><code class="lang-sh"><span class="hljs-comment"># GOARCH=arm64</span>
go tool compile -S program.go
</code></pre>
<pre><code class="lang-plaintext">main.signedLess STEXT size=32 args=0x8 locals=0x0 funcid=0x0 align=0x0 leaf
  0x0000 00000 (program.go:3)       TEXT    main.signedLess(SB), LEAF|NOFRAME|ABIInternal, $0-8
  0x0000 00000 (program.go:3)       FUNCDATA        $0, gclocals·g5+hNtRBP6YXNjfog7aZjQ==(SB)
  0x0000 00000 (program.go:3)       FUNCDATA        $1, gclocals·g5+hNtRBP6YXNjfog7aZjQ==(SB)
  0x0000 00000 (program.go:3)       FUNCDATA        $5, main.signedLess.arginfo1(SB)
  0x0000 00000 (program.go:3)       FUNCDATA        $6, main.signedLess.argliveinfo(SB)
  0x0000 00000 (program.go:3)       PCDATA  $3, $1
  0x0000 00000 (program.go:4)       MOVB    R0, R2
  0x0004 00004 (program.go:4)       MOVB    R1, R1
  0x0008 00008 (program.go:4)       CMPW    R2, R1
  0x000c 00012 (program.go:4)       CSET    GT, R0
  0x0010 00016 (program.go:4)       RET     (R30)
  0x0000 02 1c 40 93 21 1c 40 93 3f 00 02 6b e0 d7 9f 9a  ..@.!.@.?..k....
  0x0010 c0 03 5f d6 00 00 00 00 00 00 00 00 00 00 00 00  .._.............
main.unsignedLess STEXT size=32 args=0x8 locals=0x0 funcid=0x0 align=0x0 leaf
  0x0000 00000 (program.go:7)       TEXT    main.unsignedLess(SB), LEAF|NOFRAME|ABIInternal, $0-8
  0x0000 00000 (program.go:7)       FUNCDATA        $0, gclocals·g5+hNtRBP6YXNjfog7aZjQ==(SB)
  0x0000 00000 (program.go:7)       FUNCDATA        $1, gclocals·g5+hNtRBP6YXNjfog7aZjQ==(SB)
  0x0000 00000 (program.go:7)       FUNCDATA        $5, main.unsignedLess.arginfo1(SB)
  0x0000 00000 (program.go:7)       FUNCDATA        $6, main.unsignedLess.argliveinfo(SB)
  0x0000 00000 (program.go:7)       PCDATA  $3, $1
  0x0000 00000 (program.go:8)       MOVBU   R0, R2
  0x0004 00004 (program.go:8)       MOVBU   R1, R1
  0x0008 00008 (program.go:8)       CMPW    R2, R1
  0x000c 00012 (program.go:8)       CSET    HI, R0
  0x0010 00016 (program.go:8)       RET     (R30)
  0x0000 02 1c 40 d3 21 1c 40 d3 3f 00 02 6b e0 97 9f 9a  ..@.!.@.?..k....
  0x0010 c0 03 5f d6 00 00 00 00 00 00 00 00 00 00 00 00  .._.............
</code></pre>
<p>As shown above, even though <code>signedA</code> and <code>unsignedA</code> are stored identically in memory, the load instructions (<code>MOVB</code> vs <code>MOVBU</code>) and the condition codes used to produce the result (<code>CSET GT</code> vs <code>CSET HI</code>) are different. This is because the compiler knows which type is used in each function and emits machine code appropriate for operating on that type. The <code>CMPW R2, R1</code> instruction sets condition flags based on the most significant bit, which may be interpreted as a sign bit or as part of the magnitude depending on the comparison that follows. The comparison itself is selected based on the type: <code>GT</code> (greater than) is used for signed comparisons, while <code>HI</code> (higher) is used for unsigned comparisons.</p>
<p>Some languages are strict about signedness. Go and Rust do not allow implicit conversions between signed and unsigned integers, which is why the examples used <code>unsafe.Pointer</code> in Go and <code>as</code> casts in Rust.</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> unsigned <span class="hljs-keyword">uint8</span> = <span class="hljs-number">-1</span>
<span class="hljs-comment">// cannot use -1 (untyped int constant) as uint8 value in assignment (overflows)</span>
</code></pre>
<p>Even in Go, mistakes are possible. For example, this code produces an infinite loop:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> i <span class="hljs-keyword">uint</span>

    <span class="hljs-comment">// ...</span>
    <span class="hljs-keyword">for</span> i = <span class="hljs-number">5</span>; i &gt;= <span class="hljs-number">0</span>; i-- {
        <span class="hljs-built_in">println</span>(i)
    }
}
</code></pre>
<p>This is why the <a target="_blank" href="https://google.github.io/styleguide/cppguide.html#Integer_Types">Google C++ Style Guide</a> and the designers of Java were historically skeptical about using unsigned integers for arithmetic, recommending them mainly for bit masks.</p>
<p>In C and C++, implicit conversions are allowed and often happen without warnings:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">uint8_t</span> unsigned_number = <span class="hljs-number">-1</span>; <span class="hljs-comment">// implicit conversion, value becomes 255</span>
</code></pre>
<p>A classic example is comparing signed and unsigned values, such as <code>if (-1 &gt; unsigned_number)</code>, which may unexpectedly evaluate to <code>true</code>.</p>
<p>Java historically had no unsigned primitives (except <code>char</code>, which is 16-bit), as the language designers considered them too error-prone.</p>
<p>Python goes even further: it has a single <code>int</code> type with arbitrary precision. When interpreting raw bytes, you must explicitly specify whether the value is signed or unsigned:</p>
<pre><code class="lang-python">number = <span class="hljs-string">b"\xff"</span>
unsigned = int.from_bytes(number, byteorder=<span class="hljs-string">"big"</span>, signed=<span class="hljs-literal">False</span>) <span class="hljs-comment"># 255</span>
signed = int.from_bytes(number, byteorder=<span class="hljs-string">"big"</span>, signed=<span class="hljs-literal">True</span>)  <span class="hljs-comment"># -1</span>
</code></pre>
<p>Now let us move on to the second question: how to correctly convert a signed integer to an unsigned one and vice versa. In modern programming languages, this is typically done using two’s complement representation, which we discussed earlier — for example, the value <code>254</code> (binary <code>11111110</code>) is interpreted as <code>-2</code>. In fact, no inverse algorithm is required here: we simply reinterpret the same sequence of bits, treating the most significant bit as part of the value rather than as an explicit <code>- / +</code> sign.</p>
<p>Another way to reason about two’s complement is the following:</p>
<ol>
<li><p>If the number is non-negative, the signed and unsigned representations are identical.</p>
</li>
<li><p>If the number is negative, the most significant bit is <code>1</code>, and the value can be computed as <code>254 − 256 = −2</code>, where <code>256</code> is <code>2^8</code>, i.e. the maximum representable value for an 8-bit number plus one.</p>
</li>
</ol>
<h2 id="heading-can-we-look-at-another-example">Can We Look at Another Example?</h2>
<p>I would like to share a few examples where incorrect handling of unsigned integers has led to bugs.</p>
<p>Consider a ClickHouse table:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">test</span>
(
    <span class="hljs-built_in">timestamp</span> DateTime,
    <span class="hljs-keyword">sign</span>      <span class="hljs-built_in">Int8</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-number">1</span>,
    <span class="hljs-keyword">value</span>     UInt8,
    <span class="hljs-keyword">comment</span>   <span class="hljs-keyword">String</span>
) <span class="hljs-keyword">ENGINE</span> = MergeTree() <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-built_in">timestamp</span>;
</code></pre>
<p>This table stores append-only events. Logical deletions are represented by <code>sign = -1</code>, while <code>value</code> is stored as <code>UInt8</code>.</p>
<p>Now we aggregate by day using a materialized view:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MATERIALIZED</span> <span class="hljs-keyword">VIEW</span> mv_test
(
    <span class="hljs-string">`day`</span>   <span class="hljs-built_in">Date</span>,
    <span class="hljs-string">`total`</span> UInt64
)
<span class="hljs-keyword">ENGINE</span> = SummingMergeTree() <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">day</span>
<span class="hljs-keyword">AS</span>
<span class="hljs-keyword">SELECT</span> toStartOfDay(<span class="hljs-built_in">timestamp</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">day</span>,
       <span class="hljs-keyword">sum</span>(<span class="hljs-keyword">value</span> * <span class="hljs-keyword">sign</span>)       <span class="hljs-keyword">AS</span> total
<span class="hljs-keyword">FROM</span> <span class="hljs-keyword">test</span>
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">day</span>;
</code></pre>
<p>We choose the <code>UInt64</code> type for <code>total</code> because we expect it to store the sum of <code>UInt8</code> value events; rollbacks should evaluate to <code>0</code> and therefore not affect the final result.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> <span class="hljs-keyword">test</span>
<span class="hljs-keyword">VALUES</span> (<span class="hljs-string">'2026-01-01 00:00:01'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-string">'number 1'</span>),
       (<span class="hljs-string">'2026-01-01 00:00:02'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-string">'number 2'</span>),
       (<span class="hljs-string">'2026-01-01 00:00:06'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">255</span>, <span class="hljs-string">'number 255, max unsigned 8-bit'</span>);

<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> mv_test <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">day</span> <span class="hljs-keyword">FORMAT</span> Vertical;

Row 1:
──────
day:   2026-01-01
total: 258
</code></pre>
<p>Everything appears to work correctly, but only while implicitly assuming that rollback timestamps always совпiding with the original events. Once this assumption is broken, either by mistake or by design — issues start to surface. For example, the "2nd event rollback" record falls on the day after the "2nd event":</p>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> <span class="hljs-keyword">test</span>
<span class="hljs-keyword">VALUES</span> (<span class="hljs-string">'2026-01-02 23:59:55'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-string">'1st event'</span>),
       (<span class="hljs-string">'2026-01-02 23:59:58'</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">1</span>, <span class="hljs-string">'1st event rollback'</span>),
       (<span class="hljs-string">'2026-01-02 23:59:59'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-string">'2nd event'</span>),
       (<span class="hljs-string">'2026-01-03 00:00:02'</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">1</span>, <span class="hljs-string">'2nd event rollback'</span>);

<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> mv_test <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">day</span> <span class="hljs-keyword">FORMAT</span> Vertical;

Row 1:
──────
day:   2026-01-01
total: 258

Row 2:
──────
day:   2026-01-02
total: 1

Row 3:
──────
day:   2026-01-03
total: 18446744073709551615
</code></pre>
<p>However, a query equivalent to the materialized view <code>mv_test</code> produces a different result:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> toStartOfDay(<span class="hljs-built_in">timestamp</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">day</span>,
       <span class="hljs-keyword">sum</span>(<span class="hljs-keyword">value</span> * <span class="hljs-keyword">sign</span>)       <span class="hljs-keyword">AS</span> total
<span class="hljs-keyword">FROM</span> <span class="hljs-keyword">test</span>
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">day</span>
<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">day</span>
<span class="hljs-keyword">FORMAT</span> Vertical;

Row 1:
──────
day:   2026-01-01 00:00:00
total: 258

Row 2:
──────
day:   2026-01-02 00:00:00
total: 1

Row 3:
──────
day:   2026-01-03 00:00:00
total: -1
</code></pre>
<p>The value <code>18446744073709551615</code> is the maximum representable value of <code>UInt64</code>, i.e. <code>2^64 − 1</code>, which corresponds to <code>-1</code> when interpreted as an unsigned number. At the same time, the second query returns the correct result of <code>-1</code>, because by default the database engine interprets <code>total</code> as a signed value.</p>
<p>We once encountered a very similar issue in a real production system and it involved money. At some point, the total amount of refunds for a single day unexpectedly exceeded the revenue accumulated over several years :)</p>
<p>However, since this is only a matter of how the result is interpreted, and the underlying bytes are physically identical, the fix can be straightforward. If the materialized view is based on a separate table, it is sufficient to change the column type using an <code>ALTER TABLE</code> operation. Otherwise, the materialized view must be recreated and fully repopulated.</p>
<h2 id="heading-what-conclusions-can-we-draw">What Conclusions Can We Draw?</h2>
<ol>
<li><p>Unsigned integers play an important role in programming, enabling efficient memory usage and self-documenting constraints.</p>
</li>
<li><p>Signed and unsigned integers are merely different interpretations of the same bit patterns.</p>
</li>
<li><p>Choosing between them requires careful consideration of language rules, implicit conversions, and edge cases.</p>
</li>
</ol>
<h2 id="heading-what-else-is-worth-reading">What Else Is Worth Reading?</h2>
<ol>
<li><p><a target="_blank" href="https://en.wikipedia.org/wiki/Unsigned_integer">Wikipedia - Unsigned integer</a></p>
</li>
<li><p><a target="_blank" href="https://en.wikipedia.org/wiki/Two%27s_complement">Wikipedia - Two's complement</a></p>
</li>
<li><p>Blog posts by <a target="_blank" href="https://jvns.ca">Julia Evans</a></p>
</li>
</ol>
<ul>
<li><p><a target="_blank" href="https://jvns.ca/blog/2017/03/21/unsigned-integers-in-c-and-cpp/">Unsigned Integers in C and C++</a></p>
</li>
<li><p><a target="_blank" href="https://jvns.ca/blog/2023/01/18/examples-of-problems-with-integers/#example-2-integer-overflow-underflow">integer overflow/underflow</a></p>
</li>
<li><p><a target="_blank" href="https://jvns.ca/blog/2023/01/18/examples-of-problems-with-integers/#aside-how-do-computers-represent-negative-integers">how do computers represent negative integers?</a></p>
</li>
<li><p><a target="_blank" href="https://jvns.ca/blog/2023/01/18/examples-of-problems-with-integers/#example-5-security-problems-because-of-integer-overflow">security problems because of integer overflow</a></p>
</li>
<li><p><a target="_blank" href="https://jvns.ca/blog/2023/01/18/examples-of-problems-with-integers/#example-8-compilers-removing-integer-overflow-checks">compilers removing integer overflow checks</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Беззнаковые целые числа в программировании]]></title><description><![CDATA[Зачем они нужны?
Это небольшой рассказ о беззнаковых целых числах (unsigned integers) в программировании.
С математической точки зрения, это немного странная конструкция, зачем вообще нужны какие-то "урезанные" числа?
Но всё-таки для этого есть причи...]]></description><link>https://z0rr0.blog/unsigned-integers</link><guid isPermaLink="true">https://z0rr0.blog/unsigned-integers</guid><category><![CDATA[Go Language]]></category><dc:creator><![CDATA[Aleksand Zaitsev]]></dc:creator><pubDate>Fri, 09 Jan 2026 19:23:53 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767986430084/13a46063-7bec-4d3a-adad-0b1af9533c1e.jpeg" alt class="image--center mx-auto" /></p>
<h2 id="heading-zachem-oni-nuzhny">Зачем они нужны?</h2>
<p>Это небольшой рассказ о беззнаковых целых числах (unsigned integers) в программировании.
С математической точки зрения, это немного странная конструкция, зачем вообще нужны какие-то "урезанные" числа?
Но всё-таки для этого есть причины, приведу основные из них:</p>
<ol>
<li><strong>Расширение доступного диапазона чисел</strong> на том же числе бит**. Можно хранить больше значений, если отрицательные вам не нужны. Например, 8-битный знаковый целый тип может хранить значения от -128 до 127, тогда как беззнаковый тип может хранить значения от 0 до 255.</li>
<li><strong>Дополнительный контроль бизнес-логики</strong>. Иногда требования приложения не допускают отрицательных значений (например, количество единиц товара, возраст клиента, размер файла и т.д.). Тогда использование беззнаковых типов помогает явно выразить эти ограничения и предотвратить ошибки, связанные с неправильным использованием отрицательных чисел.</li>
<li><strong>Оптимизация производительности</strong>. В некоторых архитектурах процессоров операции с беззнаковыми числами могут выполняться быстрее, чем со знаковыми. Кроме того, это подсказка для компилятора или интерпретатора о том, что можно использовать определённые оптимизации.</li>
</ol>
<h2 id="heading-kak-oni-rabotayut">Как они работают?</h2>
<p>Давайте напишем небольшие примеры, чтобы наглядно посмотреть как работают беззнаковые целые числа.</p>
<p>Пример на Go - программа берёт значения от 0 до 259 и выводит их представление в виде знаковых и беззнаковых чисел, а также их бинарное и шестнадцатеричное представление.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"unsafe"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">s2u</span><span class="hljs-params">(s <span class="hljs-keyword">int8</span>)</span> <span class="hljs-title">uint8</span></span> {
    <span class="hljs-keyword">return</span> *(*<span class="hljs-keyword">uint8</span>)(unsafe.Pointer(&amp;s))
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> (
        signed   <span class="hljs-keyword">int8</span>
        unsigned <span class="hljs-keyword">uint8</span>
    )

    size := unsafe.Sizeof(signed)
    fmt.Printf(<span class="hljs-string">"Size of int8 and uint8: %d byte(s)\n\n"</span>, size)

    <span class="hljs-built_in">println</span>(<span class="hljs-string">"+---------------------------------------------------------------------------------+"</span>)
    <span class="hljs-built_in">println</span>(<span class="hljs-string">"|                    Compare signed and unsigned numbers                          |"</span>)
    <span class="hljs-built_in">println</span>(<span class="hljs-string">"+-----+----------+----------+-----+--------+-----------+-----+--------------------+"</span>)
    <span class="hljs-built_in">println</span>(<span class="hljs-string">"| #   | unsigned |   bin    | hex | signed |    bin    | hex | signed as unsigned |"</span>)
    <span class="hljs-built_in">println</span>(<span class="hljs-string">"|-----|----------|----------|-----|--------|-----------|-----|--------------------|"</span>)

    <span class="hljs-keyword">for</span> i := <span class="hljs-keyword">range</span> <span class="hljs-number">260</span> {
        signed = <span class="hljs-keyword">int8</span>(i)
        unsigned = <span class="hljs-keyword">uint8</span>(i)
        signedAsUnsigned := s2u(signed)

        fmt.Printf(<span class="hljs-string">"| %-3[1]d | %-8[2]d | %-8[2]b | %-3[2]x | %-6[3]d | %-9[3]b | %-3[3]x | %-18[4]b |\n"</span>, i, unsigned, signed, signedAsUnsigned)
    }

    <span class="hljs-built_in">println</span>(<span class="hljs-string">"+-----+----------+----------+-----+--------+-----------+-----+--------------------+"</span>)
}
</code></pre>
<p>Вывод будет следующим (я немерено сократил его для наглядности, вставив в удалённые места <code>...</code>):</p>
<pre><code>Size <span class="hljs-keyword">of</span> int8 and uint8: <span class="hljs-number">1</span> byte(s)

+---------------------------------------------------------------------------------+
|                    Compare signed and unsigned numbers                          |
+-----+----------+----------+-----+--------+-----------+-----+--------------------+
| #   | unsigned |   bin    | hex | signed |    bin    | hex | signed <span class="hljs-keyword">as</span> unsigned |
|-----|----------|----------|-----|--------|-----------|-----|--------------------|
| <span class="hljs-number">0</span>   | <span class="hljs-number">0</span>        | <span class="hljs-number">0</span>        | <span class="hljs-number">0</span>   | <span class="hljs-number">0</span>      | <span class="hljs-number">0</span>         | <span class="hljs-number">0</span>   | <span class="hljs-number">0</span>                  |
| <span class="hljs-number">1</span>   | <span class="hljs-number">1</span>        | <span class="hljs-number">1</span>        | <span class="hljs-number">1</span>   | <span class="hljs-number">1</span>      | <span class="hljs-number">1</span>         | <span class="hljs-number">1</span>   | <span class="hljs-number">1</span>                  |
| <span class="hljs-number">2</span>   | <span class="hljs-number">2</span>        | <span class="hljs-number">10</span>       | <span class="hljs-number">2</span>   | <span class="hljs-number">2</span>      | <span class="hljs-number">10</span>        | <span class="hljs-number">2</span>   | <span class="hljs-number">10</span>                 |
...
| <span class="hljs-number">126</span> | <span class="hljs-number">126</span>      | <span class="hljs-number">1111110</span>  | <span class="hljs-number">7</span>e  | <span class="hljs-number">126</span>    | <span class="hljs-number">1111110</span>   | <span class="hljs-number">7</span>e  | <span class="hljs-number">1111110</span>            |
| <span class="hljs-number">127</span> | <span class="hljs-number">127</span>      | <span class="hljs-number">1111111</span>  | <span class="hljs-number">7</span>f  | <span class="hljs-number">127</span>    | <span class="hljs-number">1111111</span>   | <span class="hljs-number">7</span>f  | <span class="hljs-number">1111111</span>            |
| <span class="hljs-number">128</span> | <span class="hljs-number">128</span>      | <span class="hljs-number">10000000</span> | <span class="hljs-number">80</span>  | <span class="hljs-number">-128</span>   | <span class="hljs-number">-10000000</span> | <span class="hljs-number">-80</span> | <span class="hljs-number">10000000</span>           |
| <span class="hljs-number">129</span> | <span class="hljs-number">129</span>      | <span class="hljs-number">10000001</span> | <span class="hljs-number">81</span>  | <span class="hljs-number">-127</span>   | <span class="hljs-number">-1111111</span>  | <span class="hljs-number">-7</span>f | <span class="hljs-number">10000001</span>           |
| <span class="hljs-number">130</span> | <span class="hljs-number">130</span>      | <span class="hljs-number">10000010</span> | <span class="hljs-number">82</span>  | <span class="hljs-number">-126</span>   | <span class="hljs-number">-1111110</span>  | <span class="hljs-number">-7</span>e | <span class="hljs-number">10000010</span>           |
...
| <span class="hljs-number">254</span> | <span class="hljs-number">254</span>      | <span class="hljs-number">11111110</span> | fe  | <span class="hljs-number">-2</span>     | <span class="hljs-number">-10</span>       | <span class="hljs-number">-2</span>  | <span class="hljs-number">11111110</span>           |
| <span class="hljs-number">255</span> | <span class="hljs-number">255</span>      | <span class="hljs-number">11111111</span> | ff  | <span class="hljs-number">-1</span>     | <span class="hljs-number">-1</span>        | <span class="hljs-number">-1</span>  | <span class="hljs-number">11111111</span>           |
| <span class="hljs-number">256</span> | <span class="hljs-number">0</span>        | <span class="hljs-number">0</span>        | <span class="hljs-number">0</span>   | <span class="hljs-number">0</span>      | <span class="hljs-number">0</span>         | <span class="hljs-number">0</span>   | <span class="hljs-number">0</span>                  |
| <span class="hljs-number">257</span> | <span class="hljs-number">1</span>        | <span class="hljs-number">1</span>        | <span class="hljs-number">1</span>   | <span class="hljs-number">1</span>      | <span class="hljs-number">1</span>         | <span class="hljs-number">1</span>   | <span class="hljs-number">1</span>                  |
| <span class="hljs-number">258</span> | <span class="hljs-number">2</span>        | <span class="hljs-number">10</span>       | <span class="hljs-number">2</span>   | <span class="hljs-number">2</span>      | <span class="hljs-number">10</span>        | <span class="hljs-number">2</span>   | <span class="hljs-number">10</span>                 |
| <span class="hljs-number">259</span> | <span class="hljs-number">3</span>        | <span class="hljs-number">11</span>       | <span class="hljs-number">3</span>   | <span class="hljs-number">3</span>      | <span class="hljs-number">11</span>        | <span class="hljs-number">3</span>   | <span class="hljs-number">11</span>                 |
+-----+----------+----------+-----+--------+-----------+-----+--------------------+
</code></pre><p>И аналогичный пример на Rust:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> size = std::mem::size_of::&lt;<span class="hljs-built_in">u8</span>&gt;();
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Size of i8 and u8: {} byte(s)\n"</span>, size);

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"+---------------------------------------------------------------------------------+"</span>);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"|                    Compare signed and unsigned numbers                          |"</span>);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"+-----+----------+----------+-----+--------+-----------+-----+--------------------+"</span>);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"| #   | unsigned |   bin    | hex | signed |    bin    | hex | signed as unsigned |"</span>);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"|-----|----------|----------|-----|--------|-----------|-----|--------------------|"</span>);

    <span class="hljs-keyword">for</span> number <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>..<span class="hljs-number">260</span> {
        <span class="hljs-comment">// cast 32 bit integer to 8 bit one</span>
        <span class="hljs-keyword">let</span> unsigned: <span class="hljs-built_in">u8</span> = number <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>; <span class="hljs-comment">// unsigned integer 8 bit</span>
        <span class="hljs-keyword">let</span> signed: <span class="hljs-built_in">i8</span> = unsigned <span class="hljs-keyword">as</span> <span class="hljs-built_in">i8</span>; <span class="hljs-comment">// signed integer 8 bit</span>
        <span class="hljs-keyword">let</span> s2u: <span class="hljs-built_in">u8</span> = signed <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>; <span class="hljs-comment">// cast signed to unsigned</span>

        <span class="hljs-built_in">println!</span>(
            <span class="hljs-string">"| {0:&lt;3} | {1:&lt;8} | {1:&lt;8b} | {1:&lt;3x} | {2:&lt;6} | {2:&lt;9b} | {2:&lt;3x} | {3:&lt;18b} |"</span>,
            number, unsigned, signed, s2u,
        );
    }

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"+-----+----------+----------+-----+--------+-----------+-----+--------------------+"</span>);
}
</code></pre>
<p>Вывод очень похож, но:</p>
<ul>
<li>В Rust у отрицательных чисел в бинарном и шестнадцатеричном представлении не выводится знак минус.</li>
<li>В Go для таких же чисел используется <a target="_blank" href="https://w.wiki/5uku">дополнительный код</a> с выводом знака минус.</li>
</ul>
<p>Но важно понимать, что это отличие лишь в представлении. В памяти оба языка хранят одни и те же данные, и в Rust тоже используется дополнительный код.</p>
<pre><code>Size <span class="hljs-keyword">of</span> i8 and u8: <span class="hljs-number">1</span> byte(s)

+---------------------------------------------------------------------------------+
|                    Compare signed and unsigned numbers                          |
+-----+----------+----------+-----+--------+-----------+-----+--------------------+
| #   | unsigned |   bin    | hex | signed |    bin    | hex | signed <span class="hljs-keyword">as</span> unsigned |
|-----|----------|----------|-----|--------|-----------|-----|--------------------|
| <span class="hljs-number">0</span>   | <span class="hljs-number">0</span>        | <span class="hljs-number">0</span>        | <span class="hljs-number">0</span>   | <span class="hljs-number">0</span>      | <span class="hljs-number">0</span>         | <span class="hljs-number">0</span>   | <span class="hljs-number">0</span>                  |
| <span class="hljs-number">1</span>   | <span class="hljs-number">1</span>        | <span class="hljs-number">1</span>        | <span class="hljs-number">1</span>   | <span class="hljs-number">1</span>      | <span class="hljs-number">1</span>         | <span class="hljs-number">1</span>   | <span class="hljs-number">1</span>                  |
| <span class="hljs-number">2</span>   | <span class="hljs-number">2</span>        | <span class="hljs-number">10</span>       | <span class="hljs-number">2</span>   | <span class="hljs-number">2</span>      | <span class="hljs-number">10</span>        | <span class="hljs-number">2</span>   | <span class="hljs-number">10</span>                 |
...
| <span class="hljs-number">126</span> | <span class="hljs-number">126</span>      | <span class="hljs-number">1111110</span>  | <span class="hljs-number">7</span>e  | <span class="hljs-number">126</span>    | <span class="hljs-number">1111110</span>   | <span class="hljs-number">7</span>e  | <span class="hljs-number">1111110</span>            |
| <span class="hljs-number">127</span> | <span class="hljs-number">127</span>      | <span class="hljs-number">1111111</span>  | <span class="hljs-number">7</span>f  | <span class="hljs-number">127</span>    | <span class="hljs-number">1111111</span>   | <span class="hljs-number">7</span>f  | <span class="hljs-number">1111111</span>            |
| <span class="hljs-number">128</span> | <span class="hljs-number">128</span>      | <span class="hljs-number">10000000</span> | <span class="hljs-number">80</span>  | <span class="hljs-number">-128</span>   | <span class="hljs-number">10000000</span>  | <span class="hljs-number">80</span>  | <span class="hljs-number">10000000</span>           |
| <span class="hljs-number">129</span> | <span class="hljs-number">129</span>      | <span class="hljs-number">10000001</span> | <span class="hljs-number">81</span>  | <span class="hljs-number">-127</span>   | <span class="hljs-number">10000001</span>  | <span class="hljs-number">81</span>  | <span class="hljs-number">10000001</span>           |
| <span class="hljs-number">130</span> | <span class="hljs-number">130</span>      | <span class="hljs-number">10000010</span> | <span class="hljs-number">82</span>  | <span class="hljs-number">-126</span>   | <span class="hljs-number">10000010</span>  | <span class="hljs-number">82</span>  | <span class="hljs-number">10000010</span>           |
...
| <span class="hljs-number">254</span> | <span class="hljs-number">254</span>      | <span class="hljs-number">11111110</span> | fe  | <span class="hljs-number">-2</span>     | <span class="hljs-number">11111110</span>  | fe  | <span class="hljs-number">11111110</span>           |
| <span class="hljs-number">255</span> | <span class="hljs-number">255</span>      | <span class="hljs-number">11111111</span> | ff  | <span class="hljs-number">-1</span>     | <span class="hljs-number">11111111</span>  | ff  | <span class="hljs-number">11111111</span>           |
| <span class="hljs-number">256</span> | <span class="hljs-number">0</span>        | <span class="hljs-number">0</span>        | <span class="hljs-number">0</span>   | <span class="hljs-number">0</span>      | <span class="hljs-number">0</span>         | <span class="hljs-number">0</span>   | <span class="hljs-number">0</span>                  |
| <span class="hljs-number">257</span> | <span class="hljs-number">1</span>        | <span class="hljs-number">1</span>        | <span class="hljs-number">1</span>   | <span class="hljs-number">1</span>      | <span class="hljs-number">1</span>         | <span class="hljs-number">1</span>   | <span class="hljs-number">1</span>                  |
| <span class="hljs-number">258</span> | <span class="hljs-number">2</span>        | <span class="hljs-number">10</span>       | <span class="hljs-number">2</span>   | <span class="hljs-number">2</span>      | <span class="hljs-number">10</span>        | <span class="hljs-number">2</span>   | <span class="hljs-number">10</span>                 |
| <span class="hljs-number">259</span> | <span class="hljs-number">3</span>        | <span class="hljs-number">11</span>       | <span class="hljs-number">3</span>   | <span class="hljs-number">3</span>      | <span class="hljs-number">11</span>        | <span class="hljs-number">3</span>   | <span class="hljs-number">11</span>                 |
+-----+----------+----------+-----+--------+-----------+-----+--------------------+
</code></pre><p>Нужно сделать небольшое отступление о дополнительном коде. В очень упрощенном варианте алгоритм его получения можно описать так (на примере числа <code>-2</code>):</p>
<ul>
<li>Как мы видим для программы на Rust, бинарное представление <code>-2</code> это <code>11111110</code>.</li>
<li>Старший разряд (самая левая цифра) отвечает за знак числа: <code>0</code> - положительное, <code>1</code> - отрицательное число.</li>
<li>Если старший разряд равен <code>0</code>, то дополнительный код совпадает с прямым бинарным представлением числа.</li>
<li>Иначе, чтобы получить дополнительный код отрицательного числа, нужно инвертировать все разряды, кроме старшего, получаем <code>10000001</code> и прибавить <code>1</code>, это будет <code>10000010</code>. Первый бит интерпретируется как знак, а остальные <code>0000010</code> это уже само число <code>2</code>. В итоге получаем <code>-2</code>.</li>
</ul>
<p>Вернёмся к выводам программ, глядя на них, возникают 2 вопроса:</p>
<ol>
<li>Как при одном и том же бинарном представлении одно и то же значение может интерпретироваться компьютером по-разному? Ведь в памяти это идентичные наборы нулей и единиц.</li>
<li>Как правильно рассчитать знакое значение из беззнакового и наоборот?</li>
</ol>
<p>Попробуем ответить на 1-й вопрос. Действительно разницы нет, но при компиляции или интерпретации кода, анализатор программы помечает какой тип используется в конкретном месте, в зависимости от этого, набор нулей и единиц выводится по-разному. По сути, информация о типе хранится в условных "метаданных" программы, а не в самих этих данных.</p>
<p>Рассмотрим на примере Go ассемблера такую программу <code>program.go</code>:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">signedLess</span><span class="hljs-params">(a, b <span class="hljs-keyword">int8</span>)</span> <span class="hljs-title">bool</span></span> {
    <span class="hljs-keyword">return</span> a &lt; b
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">unsignedLess</span><span class="hljs-params">(a, b <span class="hljs-keyword">uint8</span>)</span> <span class="hljs-title">bool</span></span> {
    <span class="hljs-keyword">return</span> a &lt; b
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> (
        signedA   <span class="hljs-keyword">int8</span>  = <span class="hljs-number">-1</span>
        signedB   <span class="hljs-keyword">int8</span>  = <span class="hljs-number">2</span>
        unsignedA <span class="hljs-keyword">uint8</span> = <span class="hljs-number">255</span>
        unsignedB <span class="hljs-keyword">uint8</span> = <span class="hljs-number">2</span>
    )

    <span class="hljs-built_in">println</span>(signedLess(signedA, signedB))
    <span class="hljs-built_in">println</span>(unsignedLess(unsignedA, unsignedB))
}
</code></pre>
<p>А затем посмотрим на ее ассемблерный вывод: </p>
<pre><code class="lang-sh"><span class="hljs-comment"># GOARCH=arm64</span>
go tool compile -S program.go
</code></pre>
<pre><code>main.signedLess STEXT size=<span class="hljs-number">32</span> args=<span class="hljs-number">0x8</span> locals=<span class="hljs-number">0x0</span> funcid=<span class="hljs-number">0x0</span> align=<span class="hljs-number">0x0</span> leaf
  <span class="hljs-number">0x0000</span> <span class="hljs-number">00000</span> (program.go:<span class="hljs-number">3</span>)       TEXT    main.signedLess(SB), LEAF|NOFRAME|ABIInternal, $<span class="hljs-number">0</span><span class="hljs-number">-8</span>
  <span class="hljs-number">0x0000</span> <span class="hljs-number">00000</span> (program.go:<span class="hljs-number">3</span>)       FUNCDATA        $<span class="hljs-number">0</span>, gclocals·g5+hNtRBP6YXNjfog7aZjQ==(SB)
  <span class="hljs-number">0x0000</span> <span class="hljs-number">00000</span> (program.go:<span class="hljs-number">3</span>)       FUNCDATA        $<span class="hljs-number">1</span>, gclocals·g5+hNtRBP6YXNjfog7aZjQ==(SB)
  <span class="hljs-number">0x0000</span> <span class="hljs-number">00000</span> (program.go:<span class="hljs-number">3</span>)       FUNCDATA        $<span class="hljs-number">5</span>, main.signedLess.arginfo1(SB)
  <span class="hljs-number">0x0000</span> <span class="hljs-number">00000</span> (program.go:<span class="hljs-number">3</span>)       FUNCDATA        $<span class="hljs-number">6</span>, main.signedLess.argliveinfo(SB)
  <span class="hljs-number">0x0000</span> <span class="hljs-number">00000</span> (program.go:<span class="hljs-number">3</span>)       PCDATA  $<span class="hljs-number">3</span>, $<span class="hljs-number">1</span>
  <span class="hljs-number">0x0000</span> <span class="hljs-number">00000</span> (program.go:<span class="hljs-number">4</span>)       MOVB    R0, R2
  <span class="hljs-number">0x0004</span> <span class="hljs-number">00004</span> (program.go:<span class="hljs-number">4</span>)       MOVB    R1, R1
  <span class="hljs-number">0x0008</span> <span class="hljs-number">00008</span> (program.go:<span class="hljs-number">4</span>)       CMPW    R2, R1
  <span class="hljs-number">0x000c</span> <span class="hljs-number">00012</span> (program.go:<span class="hljs-number">4</span>)       CSET    GT, R0
  <span class="hljs-number">0x0010</span> <span class="hljs-number">00016</span> (program.go:<span class="hljs-number">4</span>)       RET     (R30)
  <span class="hljs-number">0x0000</span> <span class="hljs-number">02</span> <span class="hljs-number">1</span>c <span class="hljs-number">40</span> <span class="hljs-number">93</span> <span class="hljs-number">21</span> <span class="hljs-number">1</span>c <span class="hljs-number">40</span> <span class="hljs-number">93</span> <span class="hljs-number">3</span>f <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">6</span>b e0 d7 <span class="hljs-number">9</span>f <span class="hljs-number">9</span>a  ..@.!.@.?..k....
  <span class="hljs-number">0x0010</span> c0 <span class="hljs-number">03</span> <span class="hljs-number">5</span>f d6 <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span>  .._.............
main.unsignedLess STEXT size=<span class="hljs-number">32</span> args=<span class="hljs-number">0x8</span> locals=<span class="hljs-number">0x0</span> funcid=<span class="hljs-number">0x0</span> align=<span class="hljs-number">0x0</span> leaf
  <span class="hljs-number">0x0000</span> <span class="hljs-number">00000</span> (program.go:<span class="hljs-number">7</span>)       TEXT    main.unsignedLess(SB), LEAF|NOFRAME|ABIInternal, $<span class="hljs-number">0</span><span class="hljs-number">-8</span>
  <span class="hljs-number">0x0000</span> <span class="hljs-number">00000</span> (program.go:<span class="hljs-number">7</span>)       FUNCDATA        $<span class="hljs-number">0</span>, gclocals·g5+hNtRBP6YXNjfog7aZjQ==(SB)
  <span class="hljs-number">0x0000</span> <span class="hljs-number">00000</span> (program.go:<span class="hljs-number">7</span>)       FUNCDATA        $<span class="hljs-number">1</span>, gclocals·g5+hNtRBP6YXNjfog7aZjQ==(SB)
  <span class="hljs-number">0x0000</span> <span class="hljs-number">00000</span> (program.go:<span class="hljs-number">7</span>)       FUNCDATA        $<span class="hljs-number">5</span>, main.unsignedLess.arginfo1(SB)
  <span class="hljs-number">0x0000</span> <span class="hljs-number">00000</span> (program.go:<span class="hljs-number">7</span>)       FUNCDATA        $<span class="hljs-number">6</span>, main.unsignedLess.argliveinfo(SB)
  <span class="hljs-number">0x0000</span> <span class="hljs-number">00000</span> (program.go:<span class="hljs-number">7</span>)       PCDATA  $<span class="hljs-number">3</span>, $<span class="hljs-number">1</span>
  <span class="hljs-number">0x0000</span> <span class="hljs-number">00000</span> (program.go:<span class="hljs-number">8</span>)       MOVBU   R0, R2
  <span class="hljs-number">0x0004</span> <span class="hljs-number">00004</span> (program.go:<span class="hljs-number">8</span>)       MOVBU   R1, R1
  <span class="hljs-number">0x0008</span> <span class="hljs-number">00008</span> (program.go:<span class="hljs-number">8</span>)       CMPW    R2, R1
  <span class="hljs-number">0x000c</span> <span class="hljs-number">00012</span> (program.go:<span class="hljs-number">8</span>)       CSET    HI, R0
  <span class="hljs-number">0x0010</span> <span class="hljs-number">00016</span> (program.go:<span class="hljs-number">8</span>)       RET     (R30)
  <span class="hljs-number">0x0000</span> <span class="hljs-number">02</span> <span class="hljs-number">1</span>c <span class="hljs-number">40</span> d3 <span class="hljs-number">21</span> <span class="hljs-number">1</span>c <span class="hljs-number">40</span> d3 <span class="hljs-number">3</span>f <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">6</span>b e0 <span class="hljs-number">97</span> <span class="hljs-number">9</span>f <span class="hljs-number">9</span>a  ..@.!.@.?..k....
  <span class="hljs-number">0x0010</span> c0 <span class="hljs-number">03</span> <span class="hljs-number">5</span>f d6 <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span>  .._.............
</code></pre><p>Как видно выше, несмотря на то, что <code>signedA</code> и <code>unsignedA</code> в памяти хранятся одинаково, операции загрузки данных <code>MOVB</code> / <code>MOVU</code> и установки результата <code>CSET GT</code> / <code>CSET HI</code> различаются. Это происходит потому, что компилятор знает, какой тип используется в каждой функции, и генерирует соответствующий машинный код для выполнения операций с этими типами. Сравнение <code>CMPW R2, R1</code> будет считать числа отрицательными или положительными в зависимости от старшего бита. Сама операция сравнения тоже выбирается в зависимости от типа: для знаковых чисел используется <code>GT</code> (greater than), а для беззнаковых <code>HI</code> (higher).</p>
<p>Некоторые языки относятся строго к типизации со знаком или без. Например, Go или Rust не даст вам неявно преобразовать знаковое число в беззнаковое и наоборот. Именно поэтому в программах выше использовался вызов с <code>unsafe.Pointer</code> для Go и <code>as</code> для Rust, чтобы явно указать на необходимость изменения типа:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> unsigned <span class="hljs-keyword">uint8</span> = <span class="hljs-number">-1</span>
<span class="hljs-comment">// cannot use -1 (untyped int constant) as uint8 value in assignment (overflows)</span>
</code></pre>
<p>Но и в Go можно допустить ошибку, например, получить бесконечный цикл можно так:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> i <span class="hljs-keyword">uint</span>

    <span class="hljs-comment">// ...</span>
    <span class="hljs-keyword">for</span> i = <span class="hljs-number">5</span>; i &gt;= <span class="hljs-number">0</span>; i-- {
        <span class="hljs-built_in">println</span>(i)
    }
}
</code></pre>
<p>Именно поэтому, например, <a target="_blank" href="https://google.github.io/styleguide/cppguide.html#Integer_Types">Google C++ Style Guide</a> и создатели Java (изначально) скептически относились к unsigned типам для арифметики, рекомендуя их только для битовых масок.</p>
<p>А например, в C или C++ неявное преобразование возможно, причём без предупреждений компилятора, что часто приводит к сложно детектируемым ошибкам:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">uint8_t</span> unsigned_number = <span class="hljs-number">-1</span>; <span class="hljs-comment">// неявное преобразование, будет равно 255</span>
</code></pre>
<p>Ещё классический пример - это сравнение знакового и беззнакового числа <code>if (-1 &gt; unsigned_number)</code>, когда можно неожиданно получить <code>true</code>, так как <code>-1</code> будет интерпретировано как большое беззнаковое число.</p>
<p>В Java долгое время вообще не было беззнаковых примитивов (кроме <code>char</code>, который 16-битный), потому что создатели языка считали, что это слишком сложно для программистов.</p>
<p>В Python всё ещё более свободно, там нет отдельных типов для знаковых и беззнаковых чисел, есть только <code>int</code>, который может принимать любые значения в пределах доступной памяти. Но как мы уже узнали выше, если данные для представления не отличаются, то как быть, когда нужно из байт всё-таки получить число, ведь оно может быть разным? Для это требуется явно указать, какой тип мы ожидаем получить:</p>
<pre><code class="lang-python">number = <span class="hljs-string">b"\xff"</span>
unsigned = int.from_bytes(number, byteorder=<span class="hljs-string">"big"</span>, signed=<span class="hljs-literal">False</span>) <span class="hljs-comment"># беззнаковое целое 255</span>
signed = int.from_bytes(number, byteorder=<span class="hljs-string">"big"</span>, signed=<span class="hljs-literal">True</span>)  <span class="hljs-comment"># знаковое целое -1</span>
</code></pre>
<p>Теперь перейдём ко 2-му вопросу, как правильно преобразовать знаковое число в беззнаковое и наоборот. В современных языках обычно используется дополнительный код, о котором мы писали выше, когда <code>254</code> (бинарное <code>11111110</code>) становится <code>-2</code>. Обратный алгоритм тут кстати не нужен, мы просто интерпретируем набор нулей и единиц как есть, учитывая старший бит как часть числа, а не знак <code>- / +</code>.</p>
<p>Ещё один способ посчитать дополнительный код:</p>
<ol>
<li>Если число положительное, то беззнаковое и знаковое представление совпадают</li>
<li>Если число отрицательное, старший бит равен <code>1</code>, тогда <code>254 - 256 = -2</code>, где <code>256</code> это <code>2^8</code>, то есть максимально возможное значение для 8-битного числа плюс 1.</li>
</ol>
<h2 id="heading-mozhno-eshyo-odin-primer">Можно ещё один пример?</h2>
<p>Хочется поделиться примерами, когда неправильная работа с беззаковыми числами привела к ошибкам.</p>
<p>Пусть у нас есть Clickhouse база данных с таблицей вида:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">test</span>
(
    <span class="hljs-built_in">timestamp</span> DateTime,
    <span class="hljs-keyword">sign</span>      <span class="hljs-built_in">Int8</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-number">1</span>,
    <span class="hljs-keyword">value</span>     UInt8,
    <span class="hljs-keyword">comment</span>   <span class="hljs-keyword">String</span>
) <span class="hljs-keyword">ENGINE</span> = MergeTree() <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-built_in">timestamp</span>;
</code></pre>
<p>Это какие-то события во времени, а так как данные append-only, то есть мы их только добавляем, то для отмены (условное удаление) используется признак <code>sign</code>, который может быть <code>1</code> или <code>-1</code>, а само значение <code>value</code> хранится в беззнаковом типе <code>UInt8</code>.</p>
<p>Теперь нам хочется агрегировать события по дням и мы создаем materialized view:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MATERIALIZED</span> <span class="hljs-keyword">VIEW</span> mv_test
(
    <span class="hljs-string">`day`</span>   <span class="hljs-built_in">Date</span>,
    <span class="hljs-string">`total`</span> UInt64
)
<span class="hljs-keyword">ENGINE</span> = SummingMergeTree() <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">day</span>
<span class="hljs-keyword">AS</span>
<span class="hljs-keyword">SELECT</span> toStartOfDay(<span class="hljs-built_in">timestamp</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">day</span>,
       <span class="hljs-keyword">sum</span>(<span class="hljs-keyword">value</span> * <span class="hljs-keyword">sign</span>)       <span class="hljs-keyword">AS</span> total
<span class="hljs-keyword">FROM</span> <span class="hljs-keyword">test</span>
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">day</span>;
</code></pre>
<p>Для <code>total</code> мы выбираем тип <code>UInt64</code>, так как ожидаем, что это сумма событий <code>value Uint8</code>, отмены при этом должны нам дать <code>0</code> и не влияют на итоговый результат.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> <span class="hljs-keyword">test</span>
<span class="hljs-keyword">VALUES</span> (<span class="hljs-string">'2026-01-01 00:00:01'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-string">'number 1'</span>),
       (<span class="hljs-string">'2026-01-01 00:00:02'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-string">'number 2'</span>),
       (<span class="hljs-string">'2026-01-01 00:00:06'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">255</span>, <span class="hljs-string">'number 255, max unsigned 8-bit'</span>);

<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> mv_test <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">day</span> <span class="hljs-keyword">FORMAT</span> Vertical;

Row 1:
──────
day:   2026-01-01
total: 258
</code></pre>
<p>Вроде бы всё хорошо, но лишь до того момента, пока мы не забыли или специально решили, что время отмены не совпадает со временем основного события. Например, запись для '2nd event rollback' попала на следующим день после '2nd event':</p>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> <span class="hljs-keyword">test</span>
<span class="hljs-keyword">VALUES</span> (<span class="hljs-string">'2026-01-02 23:59:55'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-string">'1st event'</span>),
       (<span class="hljs-string">'2026-01-02 23:59:58'</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">1</span>, <span class="hljs-string">'1st event rollback'</span>),
       (<span class="hljs-string">'2026-01-02 23:59:59'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-string">'2nd event'</span>),
       (<span class="hljs-string">'2026-01-03 00:00:02'</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">1</span>, <span class="hljs-string">'2nd event rollback'</span>);

<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> mv_test <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">day</span> <span class="hljs-keyword">FORMAT</span> Vertical;

Row 1:
──────
day:   2026-01-01
total: 258

Row 2:
──────
day:   2026-01-02
total: 1

Row 3:
──────
day:   2026-01-03
total: 18446744073709551615
</code></pre>
<p>Но запрос аналогичный MV <code>mv_test</code> показывает другой результат:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> toStartOfDay(<span class="hljs-built_in">timestamp</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">day</span>,
       <span class="hljs-keyword">sum</span>(<span class="hljs-keyword">value</span> * <span class="hljs-keyword">sign</span>)       <span class="hljs-keyword">AS</span> total
<span class="hljs-keyword">FROM</span> <span class="hljs-keyword">test</span>
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">day</span>
<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">day</span>
<span class="hljs-keyword">FORMAT</span> Vertical;

Row 1:
──────
day:   2026-01-01 00:00:00
total: 258

Row 2:
──────
day:   2026-01-02 00:00:00
total: 1

Row 3:
──────
day:   2026-01-03 00:00:00
total: -1
</code></pre>
<p>Значение <code>18446744073709551615</code> это максимально возможное значение для <code>UInt64 = 2^64 - 1</code>, то есть <code>-1</code>, интерпретированное как беззнаковое число. При этом 2-й запрос показывает правильный результат <code>-1</code>, так как по умолчанию СУБД интерпретирует <code>total</code> как знаковое число.</p>
<p>Похожую проблему мы поймали когда-то в реальном проекте, причём это касалось денег, неожиданно сумма возвратов за день превысила выручку за несколько лет :)</p>
<p>Но так как это лишь интерпретация вывода данных, а физически байты идентичны, то если MV создано на основе отдельной таблицы, достаточно изменить тип колонки через операцию <code>ALTER TABLE</code>. Иначе придётся пересоздавать MV заново и перезаполнять его.</p>
<h2 id="heading-kakie-vyvody-mozhno-sdelat">Какие выводы можно сделать?</h2>
<ol>
<li>Беззнаковые числа играют важную роль в программировании, позволяя эффективно использовать память и самодокументировать логику программ.</li>
<li>Важно понимать, что беззнаковые и знаковые числа это просто разные способы интерпретации одного и того же набора бит.</li>
<li>Нужно быть предельно аккуратными при выборе подобного типа данных, понимая ограничения используемого языка и то, как он делает преобразования между типами.</li>
</ol>
<h2 id="heading-chto-eshyo-pochitat">Что ещё почитать?</h2>
<ol>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/Unsigned_integer">Wikipedia - Unsigned integer</a></li>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/Two%27s_complement">Wikipedia - Two's complement</a></li>
<li>Примеры из <a target="_blank" href="https://jvns.ca">блога Julia Evans</a><ul>
<li><a target="_blank" href="https://jvns.ca/blog/2017/03/21/unsigned-integers-in-c-and-cpp/">Unsigned Integers in C and C++</a></li>
<li><a target="_blank" href="https://jvns.ca/blog/2023/01/18/examples-of-problems-with-integers/#example-2-integer-overflow-underflow">integer overflow/underflow</a></li>
<li><a target="_blank" href="https://jvns.ca/blog/2023/01/18/examples-of-problems-with-integers/#aside-how-do-computers-represent-negative-integers">how do computers represent negative integers?</a></li>
<li><a target="_blank" href="https://jvns.ca/blog/2023/01/18/examples-of-problems-with-integers/#example-5-security-problems-because-of-integer-overflow">security problems because of integer overflow</a></li>
<li><a target="_blank" href="https://jvns.ca/blog/2023/01/18/examples-of-problems-with-integers/#example-8-compilers-removing-integer-overflow-checks">compilers removing integer overflow checks</a></li>
</ul>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Iterators in Go]]></title><description><![CDATA[This post is an update of my old version Generators in Go post. On August 13th, the Go 1.23 release introduced a new feature: iterators have been added to the language. Therefore, I would like to compare their convenience and performance with my prev...]]></description><link>https://z0rr0.blog/iterators-in-go</link><guid isPermaLink="true">https://z0rr0.blog/iterators-in-go</guid><category><![CDATA[Go Language]]></category><dc:creator><![CDATA[Aleksand Zaitsev]]></dc:creator><pubDate>Wed, 21 Aug 2024 19:37:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724268888802/27c0b862-d6ab-4df1-a8ab-d03432a7e2aa.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This post is an update of my old version <a target="_blank" href="https://z0rr0.blog/go-generators">Generators in Go</a> post. On August 13th, the Go 1.23 release introduced a new feature: <a target="_blank" href="https://go.dev/doc/go1.23#iterators">iterators have been added</a> to the language. Therefore, I would like to compare their convenience and performance with my previous implementations.</p>
<p>I am intentionally skipping basic error checks, such as handling a negative step, just to keep the code concise. However, please do not omit these checks in production versions.</p>
<h2 id="heading-channel">Channel</h2>
<p>The first approach utilizes a Go channel. This is a straightforward example that employs a closure to iterate over values within an anonymous function.</p>
<pre><code class="lang-go"><span class="hljs-comment">// ChanIterator is a range generator using a channel.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ChanIterator</span><span class="hljs-params">(start, stop, step <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">chan</span> <span class="hljs-title">int</span></span> {
    c := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>, <span class="hljs-number">1</span>) <span class="hljs-comment">// add small to improve performance</span>

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> i := start; i &lt; stop; i += step {
            c &lt;- i
        }
        <span class="hljs-built_in">close</span>(c)
    }()

    <span class="hljs-keyword">return</span> c
}

<span class="hljs-comment">// usage</span>
chanIterator := ChanIterator(<span class="hljs-number">5</span>, <span class="hljs-number">20</span>, <span class="hljs-number">3</span>)
<span class="hljs-keyword">for</span> j := <span class="hljs-keyword">range</span> chanIterator {
    fmt.Printf(<span class="hljs-string">"%d "</span>, j)
}
<span class="hljs-comment">// output: 5 8 11 14 17</span>
</code></pre>
<h2 id="heading-function">Function</h2>
<p>The next example is also based on an anonymous function, but it requires a specific predefined error as a stop condition. Additionally, mutexes are necessary to ensure thread-safe usage.</p>
<pre><code class="lang-go"><span class="hljs-comment">// ErrStopIteration is an error to indicate the end of the iteration.</span>
<span class="hljs-keyword">var</span> ErrStopIteration = errors.New(<span class="hljs-string">"stop iteration"</span>)

<span class="hljs-comment">// FuncIterator is a function closure int-value iterator.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">FuncIterator</span><span class="hljs-params">(start, stop, step <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">func</span><span class="hljs-params">()</span> <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {
    <span class="hljs-keyword">var</span> (
        m sync.Mutex
        i = start
    )
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span> <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {
        m.Lock()
        <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            i += step
            m.Unlock()
        }()

        <span class="hljs-keyword">if</span> i &gt;= stop {
            <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>, ErrStopIteration
        }

        <span class="hljs-keyword">return</span> i, <span class="hljs-literal">nil</span>
    }
}

<span class="hljs-comment">// usage</span>
funcIterator := FuncIterator(<span class="hljs-number">5</span>, <span class="hljs-number">20</span>, <span class="hljs-number">3</span>)
<span class="hljs-keyword">for</span> j, err := funcIterator(); err == <span class="hljs-literal">nil</span>; j, err = funcIterator() {
     fmt.Printf(<span class="hljs-string">"%d "</span>, j)
}
<span class="hljs-comment">// output: 5 8 11 14 17</span>
</code></pre>
<h2 id="heading-structure">Structure</h2>
<p>To maintain internal state, you can use a structure instead of an anonymous function. In this case, the completion flag is of type <code>bool</code>. A similar approach could have been used in the previous example as well, but both options (using <code>error</code> or <code>bool</code>) seem more illustrative to me.</p>
<pre><code class="lang-go"><span class="hljs-comment">// StructIterator is a struct to iterate over a range of integers.</span>
<span class="hljs-keyword">type</span> StructIterator <span class="hljs-keyword">struct</span> {
    sync.Mutex
    current <span class="hljs-keyword">int</span>
    stop    <span class="hljs-keyword">int</span>
    step    <span class="hljs-keyword">int</span>
}

<span class="hljs-comment">// Next returns a new generation value and flag that it is not the end.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(g *StructIterator)</span> <span class="hljs-title">Next</span><span class="hljs-params">()</span> <span class="hljs-params">(<span class="hljs-keyword">int</span>, <span class="hljs-keyword">bool</span>)</span></span> {
    <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        g.Lock()
        g.current += g.step
        g.Unlock()
    }()
    <span class="hljs-keyword">return</span> g.current, g.current &lt; g.stop
}

<span class="hljs-comment">// usage</span>
structIterator := &amp;StructIterator{current: <span class="hljs-number">5</span>, stop: <span class="hljs-number">20</span>, step: <span class="hljs-number">3</span>}
<span class="hljs-keyword">for</span> j, ok := structIterator.Next(); ok; j, ok = structIterator.Next() {
     fmt.Printf(<span class="hljs-string">"%d "</span>, j)
}
<span class="hljs-comment">// output: 5 8 11 14 17</span>
</code></pre>
<h2 id="heading-iterator-since-go-123">Iterator (since Go 1.23)</h2>
<p>Starting with Go 1.23, it is now possible to create functional variables that can be used natively in a <code>for/range</code> loop. This functional style is somewhat unconventional for Go and can be challenging to read at first, but its use is justified by the simplicity it brings.</p>
<pre><code class="lang-go"><span class="hljs-comment">// NativeIterator is a native iterator for go since 1.23</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NativeIterator</span><span class="hljs-params">(start, stop, step <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">iter</span>.<span class="hljs-title">Seq</span>[<span class="hljs-title">int</span>]</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(yield <span class="hljs-keyword">func</span>(<span class="hljs-keyword">int</span>)</span> <span class="hljs-title">bool</span>)</span> {
        <span class="hljs-keyword">for</span> i := start; i &lt; stop; i += step {
            <span class="hljs-keyword">if</span> !yield(i) {
                <span class="hljs-keyword">return</span>
            }
        }
    }
}

<span class="hljs-comment">// usage</span>
nativeIterator := NativeIterator(<span class="hljs-number">5</span>, <span class="hljs-number">20</span>, <span class="hljs-number">3</span>)
<span class="hljs-keyword">for</span> j := <span class="hljs-keyword">range</span> nativeIterator {
     fmt.Printf(<span class="hljs-string">"%d "</span>, j)
}
<span class="hljs-comment">// output: 5 8 11 14 17</span>
</code></pre>
<h2 id="heading-benchmarks">Benchmarks</h2>
<p>I wrote simple tests for iterators ranging from 1 to 10000 with a step of 2. Let’s take a look at the results for different types of structures.</p>
<pre><code class="lang-plaintext">goos: linux
goarch: amd64
pkg: go-generators-v2
cpu: Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz
BenchmarkChanIterator-8      1742  614234 ns/op  161 B/op  2 allocs/op
BenchmarkFuncIterator-8     14692   82593 ns/op    8 B/op  1 allocs/op
BenchmarkStructIterator-8   16065   72821 ns/op   32 B/op  1 allocs/op
BenchmarkNativeIterator-8  112792   10622 ns/op    0 B/op  0 allocs/op
PASS
ok      go-generators-v2        6.414ss
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724268219292/14fa49d9-7573-48a5-8bf7-1340cf86ffa3.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Test results show that the new native solution outperforms all others. While it may take some time to get accustomed to, this feature has been eagerly anticipated, so kudos to the Go team. It’s also important to note that implementing such iterators would not have been possible without generics. Thanks to this, iterators as syntactic sugar have also been immediately added to slices and maps in the <a target="_blank" href="https://go.dev/doc/go1.23#iterators">standard packages</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Храните деньги в ...]]></title><description><![CDATA[Попытаемся в это посте понять как лучше хранить деньги в базах данных и какой тип использовать для работы с ними в коде.

А в чём вообще проблем? Что не так с числами с плавающей точкой?

Если кратко, то с ними все хорошо, но только они вообще не про...]]></description><link>https://z0rr0.blog/money-strorage</link><guid isPermaLink="true">https://z0rr0.blog/money-strorage</guid><category><![CDATA[Python]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Aleksand Zaitsev]]></dc:creator><pubDate>Thu, 14 Apr 2022 20:41:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1649968773419/IoF_2iWeO.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Попытаемся в это посте понять как лучше хранить деньги в базах данных и какой тип использовать для работы с ними в коде.</p>
<blockquote>
<p>А в чём вообще проблем? Что не так с числами с плавающей точкой?</p>
</blockquote>
<p>Если кратко, то с ними все хорошо, но только они вообще не про деньги. Приведу несколько примеров, которые сбивают с толку:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Python 3.9.7 (default, Sep 10 2021, 14:59:43)</span>
<span class="hljs-comment"># [GCC 11.2.0] on linux</span>
<span class="hljs-meta">&gt;&gt;&gt; </span><span class="hljs-number">0.1</span>+<span class="hljs-number">0.11</span>
<span class="hljs-number">0.21000000000000002</span>  <span class="hljs-comment"># почему?</span>
<span class="hljs-meta">&gt;&gt;&gt; </span><span class="hljs-number">1.3</span>+<span class="hljs-number">1.6</span>
<span class="hljs-number">2.9000000000000004</span>  <span class="hljs-comment"># что за ...?</span>
</code></pre>
<p>Если думаете, что проблема в языке Python, то ошибаетесь, вот код на Go</p>
<pre><code class="lang-go"><span class="hljs-comment">// go version go1.18 linux/amd64</span>
fmt.Printf(<span class="hljs-string">"%.6f"</span>, <span class="hljs-keyword">float32</span>(<span class="hljs-number">1.3</span>+<span class="hljs-number">1.6</span>))  <span class="hljs-comment">// 2.9000000</span>
fmt.Printf(<span class="hljs-string">"%.9f"</span>, <span class="hljs-keyword">float32</span>(<span class="hljs-number">1.3</span>+<span class="hljs-number">1.6</span>))  <span class="hljs-comment">// 2.900000095</span>
fmt.Printf(<span class="hljs-string">"%.15f"</span>, <span class="hljs-keyword">float32</span>(<span class="hljs-number">1.3</span>+<span class="hljs-number">1.6</span>)) <span class="hljs-comment">// 2.900000095367432</span>
fmt.Printf(<span class="hljs-string">"%.25f"</span>, <span class="hljs-keyword">float32</span>(<span class="hljs-number">1.3</span>+<span class="hljs-number">1.6</span>)) <span class="hljs-comment">// 2.9000000953674316406250000</span>

<span class="hljs-comment">// ещё немного "магии"</span>
fmt.Printf(<span class="hljs-string">"%f"</span>, <span class="hljs-keyword">float32</span>(<span class="hljs-number">16777217.0</span>)) <span class="hljs-comment">// 16777216.000000</span>
</code></pre>
<p>И аналогичные примеры на Си:</p>
<pre><code class="lang-c"><span class="hljs-comment">// gcc 11.2.0</span>
<span class="hljs-keyword">float</span> a;
a = <span class="hljs-number">3.2</span>;
<span class="hljs-built_in">printf</span>(<span class="hljs-string">"a=%.15f\n"</span>, a); <span class="hljs-comment">// a=3.200000047683716</span>
a = <span class="hljs-number">0.1</span>;
<span class="hljs-built_in">printf</span>(<span class="hljs-string">"a=%.9f\n"</span>, a);  <span class="hljs-comment">// a=0.100000001</span>
</code></pre>
<p>Очевидно, что с такими погрешностями в вычислениях нормально обрабатывать денежные транзакции не получится.</p>
<h2 id="heading-nemnogo-istorii">Немного истории</h2>
<p>Как это не странно, когда-то компьютеры ничего не знали о типе Float. Вычисления были целочисленными или каждый производитель ЭВМ использовал свой подход.</p>
<p>В 1975 Intel начал разработку сопроцессора для вычислений с плавающей точкой для своих микропроцессоров i8086/8 и i432. Там была достаточная интересная история про развитие идей и конкуренцию с DEC, подробнее можно прочитать <a target="_blank" href="https://web.archive.org/web/20160304114859/http://www.intel.com/content/dam/www/public/us/en/documents/case-studies/floating-point-case-study.pdf">тут</a>, но суть в том, что в 1980 уже вышел первый черновик стандарта, а в 1985 и его финальная версия <strong>IEEE 754-1985</strong>.</p>
<p><strong>IEEE 754</strong> определяется промышленный стандарт представления чисел с плавающей точкой в компьютерах. За прошедшие годы выходила еще пара его версий <a target="_blank" href="https://ieeexplore.ieee.org/document/4610935">IEEE 754-2008</a> и <a target="_blank" href="https://ieeexplore.ieee.org/document/8766229">IEEE 754-2019</a>.</p>
<p>Если очень кратно, то суть IEEE 754 в том, чтобы решить несколько проблем:</p>
<ul>
<li>в каком формате хранить числа с плавающей точкой в компьютерах (двоичное представление таких чисел)</li>
<li>какая должна быть у них точность</li>
<li>как обеспечить скорость вычисления при работе с ними </li>
<li>как много памяти они должны занимать</li>
<li>не должно быть неоднозначности в представлении таких чисел</li>
</ul>
<p>Попробуйте подумать над этими вопросами и поймете, что это не так уж и просто. А точнее <strong>невозможно отобразить бесконечное множество вещественных чисел на конечных набор байт</strong>, с которыми работает компьютер. За все приходится платить и потеря точности это как раз та самая цена.</p>
<p>Главная идея IEEE 754 в представлении чисел в научной нотации. Вы наверное уже видели такие записи <code>1234.5=1.2345e3</code> или <code>0.0054321=5.4321e-3</code>? Это короткая запись выражений:</p>
<p>$$
1234.5 = 1.2345 \cdot 10^3
$$</p>
<p>$$
0.0054321 = 5.4321 \cdot 10^{-3}
$$</p>
<p>С числами с плавающей точкой все тоже самое, только за основу взята не десятичная система исчисления, а двоичная:</p>
<p>$$<br />1234.5 \approx 1.2 \cdot 2^{10}
$$</p>
<p>$$
0.0054321 \approx 1.4 \cdot 2^{-8}
$$</p>
<p>Можно записать и более точно, тогда будет понятнее</p>
<p>$$
4 = 1 \cdot 2^{2}
$$</p>
<p>$$
16 = 1 \cdot 2^{4}
$$</p>
<p>$$
0.5 = 1 \cdot 2^{-1}
$$</p>
<p>$$
0.75 = 1.5 \cdot 2^{-1}
$$</p>
<p>$$
0.25 = 1 \cdot 2^{-2}
$$</p>
<p>$$
38.4 = 1.2 \cdot 2^5
$$</p>
<p>$$
0.0375 = 1.2 \cdot 2^{-5}
$$</p>
<p>Каждое число представляется в виде</p>
<p>$$
(-1)^{sign} \cdot (1 + fraction) \cdot 2^{(exponent - bias)}
$$</p>
<p>Тут несколько констант и 3 переменные, определяющие запись</p>
<ol>
<li><span><strong>sign</strong></span> - 1 бит под знак числа, чтобы понимать положительное оно или отрицательное. Удобно потом брать значение и использовать его в выражении <code>(-1)^sign</code> для определения знака.</li>
<li><span><strong>fraction</strong></span> - мантисса, вещественна часть числа от 0 до 1, в примере про <code>0.75 = 1.5 * 2^(-1)</code>, мантисса будет <code>0.5 = (1.5 - 1)</code>. Единицу <strong>отбрасывают</strong> для экономии памяти, так как она все равно всегда есть в нормализованной записи, то зачем ее хранить, если можно помнить о том, что её просто нужно всегда в конце добавить к результату.</li>
<li><span><strong>exponent</strong></span> - порядок, та самая степень двойки как в примерах выше, но со сдвигом на <em>bias</em>. Последний решает проблему знака, но не у самого числа, а у порядка. Например для чисел с одинарной точностью (Float32), <code>bias=127</code>, тогда экспонента -2 будет хранится как 125, а +2 как 129. Это позволяет использовать всего 8 бит и диапазон значений от 1 до 254, чтобы работать с вариантам степеней двойки от -126 до +127.</li>
</ol>
<p>Еще IEEE 754 оговаривает такие необычные комбинации как плюс/минус бесконечности (Inf) и неопределенное число (NaN), например полученное в результате деления на ноль.</p>
<h2 id="heading-privedenie-k-mashinnomu-vidu">Приведение к машинному виду</h2>
<p>Теперь попробуем конвертировать числа с плавающей точкой в машинный вид и обратно. Для простоты возьмем единичную точность, которая обычно в языках программирования представлена как тип <em>Float32</em>, где</p>
<ul>
<li>первые 23 бита определяют мантиссу (<span>fraction</span>)</li>
<li>следующие 8 бит это порядок (<span>exponent</span>)</li>
<li>и последний бит - знак числа (<span>sign</span>)</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649966393306/5k3wNhAbO.png" alt="float32.png" /></p>
<p>Используем формулы выше, но часть переменных нам уже известна sign=0, так как число 0.15625 положительное, а bias=127 это константа для Float32:</p>
<p>$$
(-1)^0 \cdot (1 + fraction) \cdot 2^{(exponent - 127)}
$$</p>
<p>Оценим число, чтобы найти остальные элементы. Тут все как с десятичной системой, нужно делить до тех пор пока не получим нормальную форму, когда результат станет от 1 до 2.</p>
<p>$$
0.15625 / 2^{-1} = 0.3125
$$</p>
<p>$$
0.15625 / 2^{-2} = 0.625
$$</p>
<p>$$
0.15625 / 2^{-3} = 1.25
$$</p>
<p>Получилось, что <code>(1 + fraction) = 1.25</code>, а <code>(exponent - 127) = -3</code>, тогда окончательный вид такой. </p>
<p>$$
(-1)^0 \cdot (1 + 0.25) \cdot 2^{(124 - 127)}
$$</p>
<ul>
<li>130 в бинарном представлении как раз <code>1111100</code></li>
<li>а 0.25 - <code>01000000000000000000000</code></li>
</ul>
<p>Представление 0.25 в бинарном виде в этом примере слишком очевидное, это 1/4. Другой вариант - это итерационное умножение на 2, когда берем целую часть, пока дробная не равна 0.</p>
<pre><code class="lang-python"><span class="hljs-number">0.25</span> * <span class="hljs-number">2</span> = <span class="hljs-number">0.5</span> <span class="hljs-comment"># берем 0, а дальше используем число 0.5 полностью</span>
<span class="hljs-number">0.5</span>  * <span class="hljs-number">2</span> = <span class="hljs-number">1.0</span> <span class="hljs-comment"># берем 1, а дальше используем десятичную часть</span>
<span class="hljs-number">0.0</span>  * <span class="hljs-number">2</span> = <span class="hljs-number">0.0</span> <span class="hljs-comment"># всё, больше уже ничего не измениться, дальше все 0</span>
<span class="hljs-comment"># ...</span>
</code></pre>
<p>Но если взять другой, не такой "гладкий" пример, то есть вероятность, что получение мантиссы никогда не сойдется. Например, возьмем число <code>43.52</code></p>
<p>$$
43.52 = (-1)^0 \cdot (1 + 0.36) \cdot 2^{(132 - 127)}
$$</p>
<pre><code class="lang-python"><span class="hljs-number">0.36</span> * <span class="hljs-number">2</span> = <span class="hljs-number">0.72</span> <span class="hljs-comment"># 0</span>
<span class="hljs-number">0.72</span> * <span class="hljs-number">2</span> = <span class="hljs-number">1.44</span> <span class="hljs-comment"># 1</span>
<span class="hljs-number">0.44</span> * <span class="hljs-number">2</span> = <span class="hljs-number">0.88</span> <span class="hljs-comment"># 0</span>
<span class="hljs-number">0.88</span> * <span class="hljs-number">2</span> = <span class="hljs-number">1.76</span> <span class="hljs-comment"># 1</span>
<span class="hljs-number">0.76</span> * <span class="hljs-number">2</span> = <span class="hljs-number">1.52</span> <span class="hljs-comment"># 1</span>
<span class="hljs-number">0.52</span> * <span class="hljs-number">2</span> = <span class="hljs-number">1.04</span> <span class="hljs-comment"># 1</span>
<span class="hljs-number">0.04</span> * <span class="hljs-number">2</span> = <span class="hljs-number">0.08</span> <span class="hljs-comment"># 0</span>
<span class="hljs-number">0.08</span> * <span class="hljs-number">2</span> = <span class="hljs-number">0.16</span> <span class="hljs-comment"># 0</span>
<span class="hljs-number">0.16</span> * <span class="hljs-number">2</span> = <span class="hljs-number">0.32</span> <span class="hljs-comment"># 0</span>
<span class="hljs-number">0.32</span> * <span class="hljs-number">2</span> = <span class="hljs-number">0.64</span> <span class="hljs-comment"># 0</span>
<span class="hljs-number">0.64</span> * <span class="hljs-number">2</span> = <span class="hljs-number">1.28</span> <span class="hljs-comment"># 1</span>
<span class="hljs-number">0.28</span> * <span class="hljs-number">2</span> = <span class="hljs-number">0.56</span> <span class="hljs-comment"># 0</span>
<span class="hljs-number">0.56</span> * <span class="hljs-number">2</span> = <span class="hljs-number">1.12</span> <span class="hljs-comment"># 1</span>
<span class="hljs-number">0.12</span> * <span class="hljs-number">2</span> = <span class="hljs-number">0.24</span> <span class="hljs-comment"># 0</span>
<span class="hljs-number">0.24</span> * <span class="hljs-number">2</span> = <span class="hljs-number">0.48</span> <span class="hljs-comment"># 0</span>
<span class="hljs-number">0.48</span> * <span class="hljs-number">2</span> = <span class="hljs-number">0.96</span> <span class="hljs-comment"># 0</span>
<span class="hljs-number">0.96</span> * <span class="hljs-number">2</span> = <span class="hljs-number">1.92</span> <span class="hljs-comment"># 1</span>
<span class="hljs-number">0.92</span> * <span class="hljs-number">2</span> = <span class="hljs-number">1.84</span> <span class="hljs-comment"># 1</span>
<span class="hljs-number">0.84</span> * <span class="hljs-number">2</span> = <span class="hljs-number">1.68</span> <span class="hljs-comment"># 1</span>
<span class="hljs-number">0.68</span> * <span class="hljs-number">2</span> = <span class="hljs-number">1.36</span> <span class="hljs-comment"># 1</span>
<span class="hljs-number">0.36</span> * <span class="hljs-number">2</span> = <span class="hljs-number">0.72</span> <span class="hljs-comment"># 0 - упс, это уже где-то было</span>
<span class="hljs-comment"># начинали с такого числа 0.36</span>
<span class="hljs-comment"># нашли 21 из 23 нужных бит, но следующие 2 это повторения</span>
<span class="hljs-number">0.72</span> * <span class="hljs-number">2</span> = <span class="hljs-number">1.44</span> <span class="hljs-comment"># 1</span>
<span class="hljs-number">0.44</span> * <span class="hljs-number">2</span> = <span class="hljs-number">0.88</span> <span class="hljs-comment"># 0</span>
<span class="hljs-comment"># точно знаем, что последовательность периодическая и не сходится,</span>
<span class="hljs-comment"># но записать больше 23 бит физически не можем</span>
<span class="hljs-comment"># это похоже на бесконечные рациональные дроби вида</span>
<span class="hljs-comment"># 5/27 = 0.1851851851851851...</span>
<span class="hljs-comment"># только у нашей мантиссы 20 бит будут повторяться бесконечно</span>
</code></pre>
<p>Получили запись <code>0 10000100 01011100001010001111011</code>,  это лишь приближение, а значит, что точного значения для числа <code>43.52</code> мы сохранить в памяти компьютера не можем, в каком-то вычислении ошибка все равно проявит себя:</p>
<pre><code class="lang-python"><span class="hljs-comment"># python</span>
<span class="hljs-meta">&gt;&gt;&gt; </span>print(<span class="hljs-string">'{:.15f}'</span>.format(<span class="hljs-number">43.52</span>))
<span class="hljs-number">43.520000000000003</span>
</code></pre>
<p>Кстати, если заметили, то последний бит у мантиссы <code>01011100001010001111011</code> был не 0 как мы посчитали, а 1. Это из-за округления, примерно так же как если бы мы пытались записать десятичное число с точностью до 2-х знаков <code>round(0.005) = 0.01</code>.</p>
<h2 id="heading-konvertirovanie-iz-mashinnogo-vida">Конвертирование из машинного вида</h2>
<p>Теперь возьмем любое число с плавающей точкой в бинарном виде и найдем его значение в десятичных дробях. Например <code>1 10000101 11101101110100101111001</code>:</p>
<ul>
<li>sign=-1, значит число отрицательное</li>
<li>(exponent - 127) = 10000101 (bin) = 133 (dec), значит exponent=6</li>
<li><code>11101101110100101111001</code> раскладываем по степеням двойки</li>
</ul>
<p>$$
1 \cdot 2^{-1} + 1 \cdot 2^{-2} + 1 \cdot 2^{-3} + 0 \cdot 2^{-4} + ... = 0.9290000200271606
$$</p>
<p>Получилось <code>-1.9290000200271606 * 2^6 = -123.45600128173828</code>. Кстати эту сумму я считал на компьютере, складывая float значения, поэтому тут уже есть дополнительная ошибка. Но начальное число для преобразования в бинарный формат я выбрал <code>-123.456</code>, ясно видим накопленные погрешности.</p>
<blockquote>
<p>А что не так с числом <code>16777217</code> из примеров в начале статьи? Почему ошибка такая большая, на 1 в целой части?</p>
</blockquote>
<pre><code class="lang-go"><span class="hljs-comment">// go</span>
fmt.Printf(<span class="hljs-string">"%f"</span>, <span class="hljs-keyword">float32</span>(<span class="hljs-number">16777217.0</span>)) <span class="hljs-comment">// 16777216.000000</span>
</code></pre>
<p>Проблема в том, что машинном виде по стандарту <strong>IEEE 754</strong> число будет представлено как <code>0 10010111 00000000000000000000000</code>, а это в точности такой же вид как и у числа <code>16777216</code>. У <code>16777217</code> мантисса полностью нулевая, а exponent=24, просто не хватило точности 23 бит для сохранения числа <code>0.0000000596046448</code>, единичный бит появляется только с 24-й позиции.</p>
<p>$$
16777217 = 1.0000000596046448 \cdot 2^{24}
$$</p>
<p>$$
16777216 = 1.0 \cdot 2^{24}
$$</p>
<h2 id="heading-sposoby-raboty-s-float-v-yazykah-programmirovaniya">Способы работы с Float в языках программирования</h2>
<p>Это все были лишь теоретические выкладки, но есть способы посмотреть на приведение чисел с плавающей точкой к бинарному виду в явном виде.</p>
<p>В C/C++ можно посмотреть на код ассемблера и найти там нужную переменную как long.</p>
<pre><code class="lang-sh">gcc -S -O0 source_file.c -o-
</code></pre>
<p>В языке Go в пакете <a target="_blank" href="https://pkg.go.dev/math#Float32bits">math</a> есть методы Float32bits/Float32frombits, которые позволяют делать конвертацию из float в uint и обратно.</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> (
    isFloat <span class="hljs-keyword">float32</span>
    isUint <span class="hljs-keyword">uint32</span>
)
isFloat = <span class="hljs-number">43.52</span>
isUint = math.Float32bits(isFloat)
<span class="hljs-comment">// float=43.520000 -&gt; binary=1000010001011100001010001111011</span>
fmt.Printf(<span class="hljs-string">"float=%f -&gt; binary=%b\n"</span>, isFloat, isUint)

isFloat = math.Float32frombits(isUint)
<span class="hljs-comment">// binary=1000010001011100001010001111011 -&gt; float=43.520000</span>
fmt.Printf(<span class="hljs-string">"binary=%b -&gt; float=%f\n"</span>, isUint, isFloat)
</code></pre>
<p>Для Python есть функция перевод массива байт (бинарного представления) во встроенный тип float <a target="_blank" href="https://ru.wikipedia.org/wiki/%D0%A7%D0%B8%D1%81%D0%BB%D0%BE_%D0%BE%D0%B4%D0%B8%D0%BD%D0%B0%D1%80%D0%BD%D0%BE%D0%B9_%D1%82%D0%BE%D1%87%D0%BD%D0%BE%D1%81%D1%82%D0%B8#Python">тут</a>.</p>
<h2 id="heading-kak-zhe-hranit-dengi">Как же хранить деньги</h2>
<p>После всего вышесказанного возможно у вас возникли вопросы.</p>
<blockquote>
<p>Если у float такие ошибки, зачем его вообще использовать? И есть ли вообще способ считать точно?</p>
</blockquote>
<ol>
<li>Числа плавающей точкой можно смело использовать, но только не для финансовых расчетов, где важна каждая копейка. Существуют много разных областей, где такая ювелирная точность не нужна. Другой важный момент это скорость работы с ними. В современных компьютерах вычисления с числами с плавающей точкой поддерживаются аппаратным сопроцессором (FPU - floating point unit), а это существенное ускорение в работе, то на что когда-то и ставил Intel.</li>
<li>Да есть способ считать более точно, исключая погрешности до приемлемых границ. Далее рассмотрим тип Decimal.</li>
</ol>
<p>Существуют языки, где поддержаны рациональные числа. Вот пример на <a target="_blank" href="https://groups.csail.mit.edu/mac/projects/scheme/">scheme</a> диалекте Lisp.</p>
<pre><code class="lang-lisp">(+ (/ 6 50) (/ 12 25))  

;Value: 3/5
</code></pre>
<p>Это эквивалентно</p>
<p>$$
\frac{6}{50} + \frac{12}{25} = 
\frac{3}{25} + \frac{12}{25} =
\frac{15}{25} = \frac{3}{5}
$$</p>
<p>Иногда это может быть очень удобно, например для числа <code>1/3</code> и понятно как это хранить, нужно структура с двумя полями. Но к сожалению возникают и проблемы. Задача нахождения общего знаменателя имеет сложность <code>О(N+M)</code>, а еще дроби могут становиться очень громоздкими.</p>
<p>В том же стандарте IEEE 754 есть раздел про Decimal формат, когда представление числа кодируется не степенью двойки, а десятки. Это уже совсем похоже на научную нотацию с буквами <code>e/E</code>. Поэтому основная формула очень похожа на реализацию для Float:</p>
<p>$$
(-1)^{sign} \cdot coefficient \cdot 10^{exponent}
$$</p>
<ul>
<li><strong>sign</strong> - знак числа</li>
<li><strong>coefficient</strong> - целый коэффициент, иногда сразу включает в себя <em>sign</em></li>
<li><strong>exponent</strong> - экспонента для степени 10, тоже целое число</li>
</ul>
<p>Хранят такое число уже не одним 32-битным куском, а составной структурой из эти 2-х или 3-х полей. Приведем примеры и будет понятнее:</p>
<pre><code><span class="hljs-number">1</span>       <span class="hljs-string">=&gt;</span> {<span class="hljs-attr">coefficient:</span> <span class="hljs-number">1</span>, <span class="hljs-attr">exponent:</span> <span class="hljs-number">0</span>}
<span class="hljs-number">2.0</span>     <span class="hljs-string">=&gt;</span> {<span class="hljs-attr">coefficient:</span> <span class="hljs-number">20</span>, <span class="hljs-attr">exponent:</span> <span class="hljs-number">-1</span>}
<span class="hljs-number">-1.2</span>    <span class="hljs-string">=&gt;</span> {<span class="hljs-attr">coefficient:</span> <span class="hljs-number">-12</span>, <span class="hljs-attr">exponent:</span> <span class="hljs-number">-1</span>}
<span class="hljs-number">123.45</span>  <span class="hljs-string">=&gt;</span> {<span class="hljs-attr">coefficient:</span> <span class="hljs-number">12345</span>, <span class="hljs-attr">exponent:</span> <span class="hljs-number">-2</span>}
<span class="hljs-number">0.5</span>     <span class="hljs-string">=&gt;</span> {<span class="hljs-attr">coefficient:</span> <span class="hljs-number">5</span>, <span class="hljs-attr">exponent:</span> <span class="hljs-number">-1</span>}
<span class="hljs-number">1e7</span>     <span class="hljs-string">=&gt;</span> {<span class="hljs-attr">coefficient:</span> <span class="hljs-number">1</span>, <span class="hljs-attr">exponent:</span> <span class="hljs-number">7</span>}
<span class="hljs-number">-0.0345</span> <span class="hljs-string">=&gt;</span> {<span class="hljs-attr">coefficient:</span> <span class="hljs-number">-345</span>, <span class="hljs-attr">exponent:</span> <span class="hljs-number">-5</span>}
</code></pre><p>Арифметика с такими структурами тоже предельно ясная, аккуратно по отдельности работаем с целыми частями и экспонентами, формируем новые значения. Можно посмотреть как это сделано в популярных языках, по коду всегда всё понятнее:</p>
<ul>
<li>в Python реализация в файле <a target="_blank" href="https://github.com/python/cpython/blob/main/Lib/_pydecimal.py">_pydecimal.py</a></li>
<li>в Go нет встроенного типа Decimal, но есть сторонние библиотеки, например <a target="_blank" href="https://github.com/shopspring/decimal/blob/master/decimal.go">github.com/shopspring/decimal</a></li>
</ul>
<p>Кстати в Python предложение о добавлении Decimal было сделано в 2003 году в <a target="_blank" href="https://peps.python.org/pep-0327/">PEP 327</a> и его тоже интересно почитать, чтобы понять основные мотивы для включения такой конструкции в язык.</p>
<p>Надеюсь, с языками программирования все стало чуть-чуть понятнее, с базами данных все точно так же. Многие СУБД уже имеют не только встроенный Float, но и Decimal, его и надо использовать для хранения денег. Если такого типа нет, то можно брать целочисленный с аналогии с парой <code>coefficient / exponent</code>, где 2-я часть фиксированная. Например договариваемся всё хранить до миллионной доли одной единицы валюты, тогда число <code>123.45</code> в БД сохраняем как <code>123450000</code> (то есть <code>exponent -6</code>). Языки программирования в основном дают возможность создавать Decimal из строк:</p>
<pre><code class="lang-python"><span class="hljs-comment"># python</span>
<span class="hljs-keyword">from</span> decimal <span class="hljs-keyword">import</span> Decimal

<span class="hljs-meta">&gt;&gt;&gt; </span>exponent = <span class="hljs-number">-6</span>
<span class="hljs-meta">&gt;&gt;&gt; </span>coefficient = <span class="hljs-number">123450000</span>
<span class="hljs-meta">&gt;&gt;&gt; </span>Decimal(<span class="hljs-string">f'<span class="hljs-subst">{coefficient}</span>e<span class="hljs-subst">{exponent}</span>'</span>)
Decimal(<span class="hljs-string">'123.450000'</span>)
</code></pre>
<h2 id="heading-tipichnye-oshibki-pri-rabote-s-dengami">Типичные ошибки при работе с деньгами</h2>
<p>Во-первых, как уже написано ранее не использовать совсем или очень аккуратно работать с типом Float.</p>
<p>Помнить про НДС (налог на добавленную стоимость), он же VAT (value-added tax). Он обычно задается в процентах и определяет какая часть пойдет на оплату услуг непосредственно, а какая является налогом. Например при НДС 20%, сумма в 600 делится на 500 и 100 соответственно. Но что, если клиент платит 100 при НДС=18%, какая величина налога?</p>
<p>$$
tax = \frac{amount \cdot rate}{1 + rate} = 
\frac{100 \cdot \frac{18}{100}}{1 + \frac{18}{100}} = 
\frac{50 \cdot 18}{59} = 
15\frac{15}{59}
$$</p>
<p>Не самое ровное число. В десятичной записи это бесконечная дробь с периодом в 58 знаков. Поэтому, во-вторых нужно выбрать тип округления и всегда его придерживаться во всех расчетах. Если считаете НДС через <a target="_blank" href="https://en.wikipedia.org/wiki/Rounding#Round_half_up">round half up</a>, то делайте этого везде.</p>
<p>И отсюда же вытекает следующая типичная ошибка. НДС группы товаров не равен НДС одного товара на такую же сумму. Возьмём цену 100 и снова НДС 18%, при округление <a target="_blank" href="https://en.wikipedia.org/wiki/Rounding#Round_half_up">round half up</a> значение для налога будет <code>round(15.2542) = 15.25</code>, то есть для 3 таких товаров <code>45.75</code>. А теперь посчитаем НДС для одной цены в 300 и это уже <code>round(45.7627) = 45.76</code>. Получили лишнюю копейку, которая для 3-х отдельных оплат не относилась бы к налогу.</p>
<p>Следующая проблема это обновление записей с базах данных. Даже если вы храните значения как Decimal то следующий запрос на самом деле может дать неожиданный результат:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">UPDATE</span> accounts 
<span class="hljs-keyword">SET</span> balance=balance+<span class="hljs-number">1.23</span> 
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">id</span>=<span class="hljs-number">100500</span>;
</code></pre>
<p>Лучше явно приводить значение к нужному типу, а не оставлять это на усмотрение базы данных, которая может воспринять <code>1.23</code> как Float.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">UPDATE</span> accounts 
<span class="hljs-keyword">SET</span> balance=balance+<span class="hljs-keyword">CAST</span>(<span class="hljs-string">'1.23'</span> <span class="hljs-keyword">AS</span> <span class="hljs-built_in">Decimal</span>(<span class="hljs-number">28</span>, <span class="hljs-number">8</span>)) 
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">id</span>=<span class="hljs-number">100500</span>;
</code></pre>
<p>Ну и последняя, совсем очевидная, но от этого не более редкая ошибка это подсчет "итого". Если вы формируете какой-то отчет, куда выводите уже округленные денежные значения и в нем есть секция для финального суммирования, то нельзя ее оставлять на самостоятельное заполнение. Сумма по округленным значениям в общем случае может отличаться от округленной суммы всех значений.</p>
<h2 id="heading-vyvody">Выводы</h2>
<p>Главный вывод про стандарт <strong>IEEE 754</strong> и его систему хранения данных в том, что точность числа не зависит от количество десятичных знаков в человекочитаемом представлении, так как все равно происходит приведение к нормальной форме. То есть большие ошибки могут быть даже на очень коротких и простых числах. Или очень сложные на вид переменные хорошо попадают в степень двойки и хранятся без погрешностей.</p>
<p>Нужно помнить, что есть еще более точные числа с плавающей точкой (double, float64). Да, в них тоже есть погрешности, но они уже значительно меньше.</p>
<p>Числа с плавающей запятой вполне могут использоваться для быстрых расчетов различных метрик, но только не финансовых.</p>
<p>Для точных расчетов денег используйте или целочисленные вычисления или тип Decimal.</p>
<p>Никогда не забывайте про округление, выберете и придерживайтесь одного его типа.</p>
<p>Явно контролируйте тип финансовых данные, не оставляйте его на автоматическое приведение базам или языкам программирования.</p>
<h2 id="heading-ssylki">Ссылки</h2>
<ol>
<li><a target="_blank" href="https://ieeexplore.ieee.org/document/8766229">754-2019 - IEEE Standard for Floating-Point Arithmetic</a></li>
<li><a target="_blank" href="http://mathcenter.oxford.emory.edu/site/cs170/ieee754/">The IEEE 754 Format</a></li>
<li>Wikipedia <a target="_blank" href="https://en.wikipedia.org/wiki/Single-precision_floating-point_format">Single-precision floating-point format</a></li>
<li><a target="_blank" href="https://habr.com/ru/post/112953/">Что нужно знать про арифметику с плавающей запятой</a></li>
<li>Журал "Хакер" статья <a target="_blank" href="https://xakep.ru/2015/01/01/vsyo-tochka-priplyli/">"Всё, точка, приплыли! Учимся работать с числами с плавающей точкой и разрабатываем альтернативу с фиксированной точностью десятичной дроби"</a>. <a target="_blank" href="https://habr.com/ru/company/xakep/blog/257897/">Версия</a> для Хабра.</li>
<li>Генри С. Уоррен <a target="_blank" href="http://www.williamspublishing.com/Books/978-5-8459-1838-3.html">"Алгоритмические трюки для программистов"</a>, 2-е издание (Henry Warren <a target="_blank" href="https://www.amazon.com/Hackers-Delight-2nd-Henry-Warren/dp/0321842685">"Hacker's Delight, 2nd Edition"</a>)</li>
<li>YouTube <a target="_blank" href="https://www.youtube.com/watch?v=U0U8Ddx4TgE">"Как работают числа с плавающей точкой"</a></li>
<li><a target="_blank" href="https://github.com/shopspring/decimal">github.com/shopspring/decimal</a></li>
<li><a target="_blank" href="https://peps.python.org/pep-0327/">PEP 327 – Decimal Data Type</a></li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Классы-итераторы и генераторы в Python]]></title><description><![CDATA[В языке программирования Python есть понятия итераторов и генераторов. Оба термина не очень сложные и часто является обычными вопросами для собеседований разработчиков. Но в данной статье хочется разобрать немного примеров как создавать подобные объе...]]></description><link>https://z0rr0.blog/python-class-iterators</link><guid isPermaLink="true">https://z0rr0.blog/python-class-iterators</guid><category><![CDATA[Python]]></category><category><![CDATA[generators]]></category><dc:creator><![CDATA[Aleksand Zaitsev]]></dc:creator><pubDate>Sun, 16 Jan 2022 11:38:00 GMT</pubDate><content:encoded><![CDATA[<p>В языке программирования Python есть понятия <a target="_blank" href="https://docs.python.org/3/glossary.html#term-iterator">итераторов</a> и <a target="_blank" href="https://docs.python.org/3/glossary.html#term-generator-iterator">генераторов</a>. Оба термина не очень сложные и часто является обычными вопросами для собеседований разработчиков. Но в данной статье хочется разобрать немного примеров как создавать подобные объекты с помощью классов, так как это достаточно мощный и удобный механизм.</p>
<h2 id="heading-iteratory-i-generatory">Итераторы и генераторы</h2>
<p>В определении на сайте <a target="_blank" href="https://docs.python.org/3/glossary.html#term-iterator">doc.python.org</a> говорится, что объект-итератор должен</p>
<ul>
<li>Содержать метод <code>__next__()</code>, вызываем встроенным методом <code>next()</code> для возвращения следующего элемента коллекции.</li>
<li>Если данных больше нет, то должно возникать исключение <code>StopIteration</code>.</li>
<li>Также обязателен метод <code>__iter__()</code> для возвращения объекта-итератора.</li>
</ul>
<p>Примеры встроенных типов, примеров итераторов это <code>list</code>, <code>tuple</code>.</p>
<p>С генераторами тоже все довольно просто. Это объекты, возвращающие функцию генерации с выражением <code>yield</code>. Её особенность в том, что при следующем вызове, функция продолжает свою работу не с самого начала, а с места использования этого служебного слова.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Generator
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generator</span>(<span class="hljs-params">start: int, stop: int, step: int = <span class="hljs-number">1</span></span>) -&gt; Generator[int, <span class="hljs-keyword">None</span>, <span class="hljs-keyword">None</span>]:</span>
    <span class="hljs-string">"""
    Генератор целых чисел от start до stop с шагом step.

    &gt;&gt;&gt; list(generator(5, 13, 2))
    [5, 7, 9, 11, 13]
    """</span>
    i = start
    <span class="hljs-keyword">while</span> i &lt;= stop:
        <span class="hljs-keyword">yield</span> i
        i += step
</code></pre>
<p>Особенно хочется подчеркнуть, что существуют удобные <a target="_blank" href="https://docs.python.org/3/glossary.html#term-generator-expression">выражения-генераторы</a>, когда отдельная функция не нужна.</p>
<h2 id="heading-klassy">Классы</h2>
<p>Посмотрим на примеры классов, которые являются итераторами. Например в стандартной библиотеке есть пакет <code>csv</code>, создав объект <a target="_blank" href="https://docs.python.org/3/library/csv.html?highlight=csv#csv.reader">reader</a>, можно итерироваться непосредственно по нему:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> csv
csv_file = open(<span class="hljs-string">'my_file.csv'</span>)
my_reader = csv.reader(csv_file)
<span class="hljs-keyword">for</span> row <span class="hljs-keyword">in</span> my_reader:
    print(row)
</code></pre>
<p>Кроме того в данном примере файл не парсится весь сразу, а данные возвращаются по ходу его чтения. Это уже поведения генератора.</p>
<p>Попытается написать класс с похожим поведением.</p>
<h3 id="heading-prostoj-generator">Простой генератор</h3>
<p>Пример класса, который может выступать генератором, возвращающим квадраты чисел из входного списка.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SquareIterator</span>:</span>
    <span class="hljs-string">"""
    R квадратов чисел.

    &gt;&gt;&gt; s = SquareIterator([3, 5, 7])
    &gt;&gt;&gt; list(s)
    [9, 25, 49]
    &gt;&gt;&gt; list(s)
    []
    """</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, data: List[int]</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
        self.index = <span class="hljs-number">0</span>
        self.total = len(data)
        self.data = data

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__iter__</span>(<span class="hljs-params">self</span>) -&gt; Iterator[int]:</span>
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__next__</span>(<span class="hljs-params">self</span>) -&gt; int:</span>
        <span class="hljs-keyword">if</span> self.index &gt;= self.total:
            <span class="hljs-keyword">raise</span> StopIteration
        value = self.data[self.index] ** <span class="hljs-number">2</span>
        self.index += <span class="hljs-number">1</span>
        <span class="hljs-keyword">return</span> value
</code></pre>
<p>Но иногда хочется иметь возможность итерироваться по коллекции несколько раз, как например мы можем это делать по типу <code>list</code>. Для этого нужно дополнить метод <code>__iter__</code> сбросом начального состояния.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SquareInfIterator</span>(<span class="hljs-params">SquareIterator</span>):</span>
    <span class="hljs-string">"""
    Генератор квадратов чисел с возможностью повторного вызова.

    &gt;&gt;&gt; s = SquareIterator([3, 5, 7])
    &gt;&gt;&gt; list(s)
    [9, 25, 49]
    &gt;&gt;&gt; list(s)
    [9б 25 49]
    """</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__iter__</span>(<span class="hljs-params">self</span>) -&gt; Iterator[int]:</span>
        self.index = <span class="hljs-number">0</span>
        <span class="hljs-keyword">return</span> self
</code></pre>
<h3 id="heading-proizvolnyj-generator">Произвольный генератор</h3>
<p>Метод <code>__iter__</code> не обязан всегда возвращать <code>self</code>. В качестве возвращаемого значения ожидается любой генератор, который потом можно использовать в качестве аргумента для <code>next()</code>:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RangeGenerator</span>:</span>
    <span class="hljs-string">"""
    Range генератор.

    &gt;&gt;&gt; r = RangeGenerator(5, 13, 2)
    &gt;&gt;&gt; list(r)
    [5, 7, 9, 11, 13]
    """</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, start: int, stop: int, step: int = <span class="hljs-number">1</span></span>) -&gt; <span class="hljs-keyword">None</span>:</span>
        self.start = start
        self.stop = stop
        self.step = step

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__iter__</span>(<span class="hljs-params">self</span>) -&gt; Generator[int, <span class="hljs-keyword">None</span>, <span class="hljs-keyword">None</span>]:</span>
        i = self.start
        <span class="hljs-keyword">while</span> i &lt;= self.stop:
            <span class="hljs-keyword">yield</span> i
            i += self.step
</code></pre>
<p>Аналогичным образом можно написать генератор, похожий на <code>csv.reader</code>, когда не нужно читать файл целиком и нам интересны операции на отдельными строками:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FileLineLenGenerator</span>:</span>
    <span class="hljs-string">"""
    Генератор длин строк файла.

    &gt;&gt;&gt; f = FileLineLenGenerator('my_file.txt')
    &gt;&gt;&gt; list(f)
    [4, 3, 8]
    """</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, file_name: str</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
        self.file_name = file_name

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__iter__</span>(<span class="hljs-params">self</span>) -&gt; Generator[int, <span class="hljs-keyword">None</span>, <span class="hljs-keyword">None</span>]:</span>
        <span class="hljs-keyword">with</span> open(self.file_name) <span class="hljs-keyword">as</span> f:
            <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> f:
                <span class="hljs-keyword">yield</span> len(line)
</code></pre>
<h2 id="heading-zaklyuchenie">Заключение</h2>
<p>Python итераторы это очень мощный инструмент языка. С помощью генераторов можно совершать "ленивые" вычисления, экономив на памяти. А возможность создавать произвольные классы итераторы делает программирование более гибким.</p>
<p>Все примеры доступны на <a target="_blank" href="https://github.com/z0rr0/blog/blob/main/posts/python-class-iterators/generator.py">github.com</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Обратная сторона технического собеседования]]></title><description><![CDATA[Последнее время мне приходится проводить много технических собеседований как в свою команду, так и целом для компании, в которой я работаю. Хочется записать некоторые мысли, идеи и советы для тех, кому это только предстоит.
Я постараюсь абстрагироват...]]></description><link>https://z0rr0.blog/interview-back-side</link><guid isPermaLink="true">https://z0rr0.blog/interview-back-side</guid><category><![CDATA[interview]]></category><dc:creator><![CDATA[Aleksand Zaitsev]]></dc:creator><pubDate>Sat, 27 Nov 2021 14:22:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1638022891330/iP6dvoG3F.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Последнее время мне приходится проводить много технических собеседований как в свою команду, так и целом для компании, в которой я работаю. Хочется записать некоторые мысли, идеи и советы для тех, кому это только предстоит.</p>
<p>Я постараюсь абстрагироваться от деталей технических задач, которые я спрашиваю, а оставлю только общие части.</p>
<p>Существует много книг и интернет ресурсов о том как проходить техническое собеседование. Гораздо реже можно узнать как его <strong>проводить</strong>. Этот пост лишь <em>личное мнение</em> и процессы в разных компаниях могут сильно отличаться. Хорошо, если у вас был специальный тренинг для проведения интервью или до этого была возможность походить просто наблюдателем на такие встречи коллег.</p>
<h2 id="heading-cel">Цель</h2>
<p>Очевидно, что сформулировать цель это самое главное. Звучит банально, но нужно понять для чего вы проводите интервью. Первое, что приходит в голову - найти <strong>нужного</strong> человека, чтобы закрыть вакансию. Благодаря этому компания сможет сделать быстрее и качественнее свой "продукт", а значит получить больше прибыли.</p>
<p>Но вся суть в этом слове <strong>нужного</strong>. Попытаемся сформулировать, кто нам нужен:</p>
<ol>
<li>он/она профессионалы своего дела</li>
<li>знают стандартные пути решения, но кроме того умеют мыслить креативно</li>
<li>могут быстро и без ошибок выполнять поставленные задачи</li>
<li>самостоятельные, соблюдает баланс между "попросить помощи" и "принять решение самостоятельно"</li>
<li>социализированы, знают как работать в команде, получать новый опыт и делиться своим с коллегами</li>
</ol>
<p>Но это лишь идеал 😕 А ещё для всех пунктов можно сделать поправку на требуемый уровень того, кого мы ищем junior, middle или senior.</p>
<p>В книге <a target="_blank" href="https://www.mann-ivanov-ferber.ru/books/rabota-rulit/">Работа рулит</a> была сформулировано очень интересная мысль о том, что самое сложное в собеседовании это не оценить текущий технический уровень кандидата, а понять какой его уровень будет именно на том месте, для которого вы проводите интервью.  </p>
<p>Человек может быть очень крутым специалистом сейчас, но после выхода на работу, не сможет или не захочет её выполнять и использовать свои возможности в полной мере. Или у него не получится работать в той команде или с теми людьми, куда его взяли. Обратное тоже верно, если у кандидата не хватает знаний сейчас, то это не значит, что он не сможет вырасти в будущем.</p>
<p>В идеальном варианте, вакансия и кандидат должны подходить друг к другу как кусочки LEGO, чтобы человек мог раскрыться и максимально проявить свои лучшие качества. Цель интервьюера - попытаться за короткое время сравнить эти "кусочки" и понять насколько они <strong>будут разными</strong>.</p>
<h2 id="heading-kandidat">Кандидат</h2>
<p>Есть несколько вещей которые нужно знать, начиная собеседование:</p>
<ol>
<li><strong>Интервью это стресс для кандидата.</strong> Поведение человека на собеседовании будет отличаться от его обычного процесса работы, иногда даже очень сильно. А еще часто это изменение видно прямо за время разговора. Я встречал людей, которые начинали панически смеяться или замыкались и плохо шли на контакт или наоборот были чересчур расслаблены. Как это понять, когда видишь человека первый раз в жизни? Часто это бросается в глаза, но все-таки общего решения нет, просто надо учитывать, что <strong>стресс есть всегда</strong>. Но это можно и использовать. Большую часть рабочего времени сотрудник будет работать в нормальном режими, но когда-нибудь ему точно придется столкнуться со стрессом, вот тогда скорее всего его поведение будет близким к тому, что вы видите на собеседовании.</li>
<li><strong>Кандидат чаще всего готовится к собеседованию.</strong> Нельзя забывать про это, когда вы получаете слишком быстрый и отточенный ответ на сложный, как вам казалось, вопрос. Попробуйте задать его позже чуть иначе, добиваясь того, чтобы кандидат применил уже имеющиеся у него знания. Другой подход - уйти в глубь вопроса, если это возможно и хватает ваших собственных знаний 😉 В одном из выпуском подкаста <a target="_blank" href="https://music.yandex.ru/album/18906737/track/95558159">YaTalks</a> было интересное сравнение собеседования со свиданием, обе стороны готовятся и это не является обманом, а скорее взаимоуважением. </li>
<li><strong>Кандидат не обязан говорить вам всю правду.</strong> Звучит странно, но это так) Иногда, прочитав очередное резюме человека с 1-2 годами опыта, ловишь себя на мысли "когда он успел освоить все эти технологии?". Но это обманчиво. Ни резюме, ни сам кандидат не скажут вам всей правды, это можно проверить лишь своими вопросами или отзывами людей, которым вы доверяете.</li>
<li><strong>Откровенность за откровенность.</strong> Это уже немного психологический подход. Не значит, что вы должны рассказать о себе всё, но не надо юлить и утаивать то, что не является секретом. Даже если что-то может оттолкнуть человека от текущей вакансии, лучше сказать все прямо, чем через несколько недель или месяцев подписывать его заявление об увольнении по собственному желанию. А если вам показалось что-то странным и можно спросить об этом корректно, не упускайте такую возможность. Не надо манипулировать и противопоставлять себя кандидату, вы в равных позициях, на открытое поведение человек открывается в ответ.</li>
</ol>
<h2 id="heading-process">Процесс</h2>
<p>Не вдаваясь в конкретику опишу как для меня выглядит общий процесс собеседования.</p>
<ol>
<li><strong>Знакомство.</strong> Краткий рассказ кандидату о вакансии, для которой проводится интервью. Его ознакомление с общим процессом и планом на эту беседу. Здесь стоит оценить первичный уровень стресса при беседе и по возможности снизить его. </li>
<li><strong>Прошлый опыт.</strong> Рассказ кандидата о своем опыте, чем уже занимался и с чем планирует работать. Это важный этап, чтобы понять мотивацию человека, оценить  привлекательные и отталкивающие для него вещи.</li>
<li><strong>Практическая часть.</strong> Здесь мы чаще всего просим кандидата решить задачу прямо при нас.<ul>
<li>Уровень задания должен соответствовать знаниям кандидата.</li>
<li>Формулировка задачи не должна быть очень сложной. На прочтение и понимание должно хватать 2-3 минут.</li>
<li>Решение задачи не должно занимать дольше 20-30 минут. Если вы сами в спокойной остановке справляетесь с ней за полчаса, то значит нужно что-то сократить, иначе взволнованному человеку уложиться в отведенное время точно не получится. Засекайте время начала решения.</li>
<li>Хорошо, если задача близка к тому, с чем будет работать кандидат, если пройдет собеседование. Не надо давать сложных алгоритмов, если в работе это не используется, лучше взять что-то реальное и адаптировать.</li>
<li>Задача должна проверять конкретные знания. Решение не должно быть слишком тривиальным или наоборот - очень специальным.</li>
<li>Хорошо, если у задания есть автоматические тесты. Это ускоряет проверку и дает её более объективную оценку.</li>
<li>Если задача была решена слишком быстро, значит вы неверно оценили уровень кандидата или ваши вопросы "утекли" в интернет и решение уже общеизвестно. На этот случай надо иметь всегда несколько разных заданий примерно одной и той же сложности.</li>
</ul>
</li>
<li><strong>Теоретическая часть.</strong> Это время для проверки теоретических знаний. Можно начать со слабых мест, которые вы увидели на практике. Конкретно для моей секции backend разработки мы обязательно спрашиваем про язык на котором пишет кандидат, базы данных, сети, unix, git и общие вопросы про безопасность.</li>
<li><strong>Заключение.</strong> Время для вопросов от кандидата. Он или она могут задавать любые вопросы, если их будет мало, то сами расскажите что-нибудь, чтобы развить интерес. Кроме того нужно проверить, что за п.1-4 нашли для себя ответы на следующие вопросы (а если нет, то тут последний шанс это сделать):<ul>
<li>кандидату понятно, чем он будет заниматься, если пройдет собеседование? </li>
<li>вам понятно его мнение о будущей работе?</li>
<li>по каким критериям кандидат будет выбирать место работы между несколькими предложениями?</li>
<li>вам понятно, что в работе нравится и не нравится кандидату?</li>
<li>на какую зарплату рассчитывает кандидат?</li>
<li>чем кандидат интересуется в свободное время (книги, хобби, статьи и др.)?</li>
</ul>
</li>
</ol>
<p>Упустил момент про деньги. Но тут все не так однозначно. Часто получается так, что это обсуждают в самом конце разговора, но есть и другая точка зрения - уточнить ожидаемый уровень зарплаты как можно раньше, чтобы сразу сопоставить это со своими ожиданиями и возможно сэкономить время и себе и кандидату.</p>
<p>Ещё важный момент - всегда ведите личные заметки сразу по ходу собеседования. И как только оно закончится, напиши все, что думаете про это. В потоке постоянных интервью вы точно забудете какие-то моменты конкретной беседы и тогда, эти записи станут бесценным напоминанием.</p>
<h2 id="heading-knigi">Книги</h2>
<p>Книги, которые хотелось бы порекомендовать тем, кто проводит собеседование:</p>
<ol>
<li><a target="_blank" href="https://www.mann-ivanov-ferber.ru/books/rabota-rulit/">Работа рулит! Почему большинство людей в мире хотят работать именно в Google.</a></li>
<li><a target="_blank" href="https://alpinabook.ru/catalog/book-drayv/">Драйв. Что на самом деле нас мотивирует.</a></li>
<li><a target="_blank" href="https://www.piter.com/product_by_id/248219750">Делай как в Google. Разработка программного обеспечения.</a></li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Generators in Go]]></title><description><![CDATA[Disclaimer
This is not an example for production code.
It is bad practice to move patterns from known programming language to new one. But sometimes you can adapt them.


Many programming languages have a special type of collection called generator. ...]]></description><link>https://z0rr0.blog/go-generators</link><guid isPermaLink="true">https://z0rr0.blog/go-generators</guid><category><![CDATA[Go Language]]></category><category><![CDATA[Benchmark]]></category><dc:creator><![CDATA[Aleksand Zaitsev]]></dc:creator><pubDate>Sat, 04 Sep 2021 17:30:52 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p><strong>Disclaimer</strong></p>
<p>This is not an example for production code.<br />
It is bad practice to move patterns from known programming language to new one. But sometimes you can adapt them.</p>
</blockquote>
<hr />
<p>Many programming languages have a special type of collection called <em>generator</em>. It’s lazy structure, that works with one item in one time. So it is more efficiency by memory but you can iterate by objects only once.</p>
<p><em>Python example</em></p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">gen</span>(<span class="hljs-params">start: int, stop: int, step: int</span>) -&gt; Generator[int, <span class="hljs-keyword">None</span>, <span class="hljs-keyword">None</span>]:</span>
    x = start
    <span class="hljs-keyword">while</span> x &lt; stop:
        <span class="hljs-keyword">yield</span> x
        x += step

g = gen(<span class="hljs-number">5</span>, <span class="hljs-number">20</span>, <span class="hljs-number">3</span>)
print(g)
<span class="hljs-comment"># &lt;generator object gen at 0x104c85f20&gt;</span>

print(list(g))
<span class="hljs-comment"># [5, 8, 11, 14, 17]</span>

<span class="hljs-comment"># second call returns empty list</span>
<span class="hljs-comment"># because there is no history of returned data</span>
print(list(g))
<span class="hljs-comment"># []</span>
</code></pre>
<p>Go language doesn't have generators in the standard library. Let’s write three different implementation of this pattern and compare them.</p>
<h3 id="channel-generator">Channel generator</h3>
<p>Go channel is a nice mechanism for concurrency communication. One goroutine sends (generates) data to a channel and reader receives the value. Every time we work only with one item.</p>
<pre><code class="lang-go"><span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-comment">// ErrOffsetIteration is error for failed step/size value.</span>
<span class="hljs-keyword">var</span> ErrOffsetIteration = errors.New(<span class="hljs-string">"iteration offset must be positive"</span>)

<span class="hljs-comment">// ChanGenerator is a range generator.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ChanGenerator</span><span class="hljs-params">(start, stop, step <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>, error)</span></span> {
    <span class="hljs-keyword">if</span> step &lt; <span class="hljs-number">1</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, ErrOffsetIteration
    }
    c := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> j := start; j &lt; stop; j += step {
            c &lt;- j
        }
        <span class="hljs-built_in">close</span>(c)
    }()
    <span class="hljs-keyword">return</span> c, <span class="hljs-literal">nil</span>
}

<span class="hljs-comment">// how to use it (without error check)</span>
g, _ := ChanGenerator(<span class="hljs-number">5</span>, <span class="hljs-number">20</span>, <span class="hljs-number">3</span>)
<span class="hljs-keyword">for</span> j := <span class="hljs-keyword">range</span> g {
    fmt.Println(j)
}
</code></pre>
<p>Many consumers can safety read generator’s data from a channel, but it should be slowly due to internal locks, we will check this later.</p>
<h3 id="function-generator">Function generator</h3>
<p>The second implementation is a function with closure. We create initial state <code>i</code> and modify it inside an internal returned anonymous function.</p>
<pre><code class="lang-go"><span class="hljs-keyword">import</span> (
    <span class="hljs-string">"sync"</span>
    <span class="hljs-string">"fmt"</span>
)

<span class="hljs-comment">// ErrStopIteration is error when iteration is finished.</span>
<span class="hljs-keyword">var</span> ErrStopIteration = errors.New(<span class="hljs-string">"stop iteration"</span>)

<span class="hljs-comment">// FuncGenerator is a function closure int generator.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">FuncGenerator</span><span class="hljs-params">(start, stop, step <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">func</span><span class="hljs-params">()</span> <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {
    <span class="hljs-keyword">if</span> step &lt; <span class="hljs-number">1</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span> <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {
            <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>, ErrOffsetIteration
        }
    }
    <span class="hljs-keyword">var</span> m sync.Mutex
    i := start
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span> <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {
        m.Lock()
        <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            i += step
            m.Unlock()
        }()
        <span class="hljs-keyword">if</span> i &gt;= stop {
            step = <span class="hljs-number">0</span>
            <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>, ErrStopIteration
        }
        <span class="hljs-keyword">return</span> i, <span class="hljs-literal">nil</span>
    }
}

<span class="hljs-comment">// how to use it</span>
<span class="hljs-keyword">var</span> (
    j   <span class="hljs-keyword">int</span>
    err error
)
g := FuncGenerator(<span class="hljs-number">5</span>, <span class="hljs-number">20</span>, <span class="hljs-number">3</span>)
<span class="hljs-keyword">for</span> j, err = g(); err == <span class="hljs-literal">nil</span>; j, err = g() {
    fmt.Println(j)
}
<span class="hljs-keyword">if</span> err != ErrStopIteration {
    fmt.Println(<span class="hljs-string">"oops"</span>)
    <span class="hljs-comment">// handle error</span>
}
</code></pre>
<p>Mutex is required here, because concurrent function calls can update <code>i</code> in several goroutines.</p>
<h3 id="struct-generator">Struct generator</h3>
<p>The latest generator example is structure. It has a current value and stores additional parameters. Also our structure must have mutex like an example before, because concurrent reading should be safety.</p>
<pre><code class="lang-go"><span class="hljs-comment">// StructGenerator is a struct generator.</span>
<span class="hljs-keyword">type</span> StructGenerator <span class="hljs-keyword">struct</span> {
    sync.Mutex
    stop  <span class="hljs-keyword">int</span>
    step  <span class="hljs-keyword">int</span>
    value <span class="hljs-keyword">int</span>
}

<span class="hljs-comment">// NewStructGenerator returns a new struct generator.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewStructGenerator</span><span class="hljs-params">(start, stop, step <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(*StructGenerator, error)</span></span> {
    <span class="hljs-keyword">if</span> step &lt; <span class="hljs-number">1</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, ErrOffsetIteration
    }
    <span class="hljs-keyword">return</span> &amp;StructGenerator{stop: stop, step: step, value: start}, <span class="hljs-literal">nil</span>
}

<span class="hljs-comment">// Next returns a new generation value and flag that it is not the end.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(g *StructGenerator)</span> <span class="hljs-title">Next</span><span class="hljs-params">()</span> <span class="hljs-params">(<span class="hljs-keyword">int</span>, <span class="hljs-keyword">bool</span>)</span></span> {
    <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        g.Lock()
        g.value += g.step
        g.Unlock()
    }()
    <span class="hljs-keyword">return</span> g.value, g.value &lt; g.stop
}

<span class="hljs-comment">// how to use it (without error handling)</span>
g, _ := NewStructGenerator(<span class="hljs-number">5</span>, <span class="hljs-number">20</span>, <span class="hljs-number">3</span>)
<span class="hljs-keyword">for</span> v, ok := g.Next(); ok; v, ok = g.Next() {
    fmt.Println(v)
}
</code></pre>
<p>It’s very easy way, it’s interesting to compare all three methods by speed and memory usage.</p>
<h2 id="benchmarks">Benchmarks</h2>
<p>I wrote simple <a target="_blank" href="https://github.com/z0rr0/blog/blob/main/posts/go-generators/example/example_test.go">example</a> using Go benchmark mechanism <code>testing.B</code> and  got results:</p>
<pre><code><span class="hljs-attribute">goos</span>: darwin
<span class="hljs-attribute">goarch</span>: amd<span class="hljs-number">64</span>
<span class="hljs-attribute">pkg</span>: github.com/z<span class="hljs-number">0</span>rr<span class="hljs-number">0</span>/blog/posts/go-generators/example
<span class="hljs-attribute">cpu</span>: Intel(R) Core(TM) i<span class="hljs-number">5</span>-<span class="hljs-number">1038</span>NG<span class="hljs-number">7</span> CPU @ <span class="hljs-number">2</span>.<span class="hljs-number">00</span>GHz
<span class="hljs-attribute">BenchmarkChanGenerator</span>-<span class="hljs-number">8</span>         <span class="hljs-number">5772</span> <span class="hljs-number">205862</span> ns/op <span class="hljs-number">145</span> B/op <span class="hljs-number">2</span> allocs/op
<span class="hljs-attribute">BenchmarkFuncGenerator</span>-<span class="hljs-number">8</span>        <span class="hljs-number">66204</span>  <span class="hljs-number">17777</span> ns/op  <span class="hljs-number">72</span> B/op <span class="hljs-number">4</span> allocs/op
<span class="hljs-attribute">BenchmarkStructGenerator_Next</span>-<span class="hljs-number">8</span> <span class="hljs-number">65004</span>  <span class="hljs-number">18028</span> ns/op  <span class="hljs-number">64</span> B/op <span class="hljs-number">1</span> allocs/op
</code></pre><p>The histograms show results more clearly</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630776197311/GkDOTLWS7.png" alt="time_usage.png" /></p>
<p>And comparing by memory usage </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630776228708/qTeOSuC3P.png" alt="memory_usage.png" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>There are different ways to implement generators in Go. Using channels is not the best idea. Of course we need to control  concurrent access, but standard mutex can do it better.</p>
<p>I also wrote examples for chunk generators then we need to split a slice into pieces. For example, we want to convert <code>[1, 2, 3,… 10]</code> to 5 chunks by 2 items <code>[1, 2], [3, 4], … [9, 10]</code>. The same ways were used for it. You can find it in repo <a target="_blank" href="https://github.com/z0rr0/blog/tree/main/posts/go-generators/example">go-generators/example</a></p>
]]></content:encoded></item><item><title><![CDATA[Python comprehensions vs cycles]]></title><description><![CDATA[Almost all python programmers know that there are list comprehensions PEP 202.
# create list of odd numbers with cycle
a = []
for i in range(10):
    if i % 2:
        a.append(i)

# create same collection using list comprehension
a = [i for i in ran...]]></description><link>https://z0rr0.blog/comprehensions-vs-cycles</link><guid isPermaLink="true">https://z0rr0.blog/comprehensions-vs-cycles</guid><category><![CDATA[Python]]></category><category><![CDATA[Python 3]]></category><dc:creator><![CDATA[Aleksand Zaitsev]]></dc:creator><pubDate>Tue, 31 Aug 2021 20:18:13 GMT</pubDate><content:encoded><![CDATA[<p>Almost all python programmers know that there are list comprehensions <a target="_blank" href="https://www.python.org/dev/peps/pep-0202/">PEP 202</a>.</p>
<pre><code class="lang-python"><span class="hljs-comment"># create list of odd numbers with cycle</span>
a = []
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">10</span>):
    <span class="hljs-keyword">if</span> i % <span class="hljs-number">2</span>:
        a.append(i)

<span class="hljs-comment"># create same collection using list comprehension</span>
a = [i <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">10</span>) <span class="hljs-keyword">if</span> i % <span class="hljs-number">2</span>]
</code></pre>
<p>It isn’t only syntax sugar, comprehensions have CPython code optimisations and work faster. But what benefits do they provide? Let’s try to evaluate this.</p>
<p>I wrote a simple python program to compare execution for operation create collections using cycles and comprehensions. We will check <code>dict</code>, <code>list</code> and <code>set</code>. Base class for benchmarking has methods</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Dict, List, Set

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BaseBench</span>:</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, n: int = <span class="hljs-number">1</span></span>) -&gt; <span class="hljs-keyword">None</span>:</span>
        self.n = n

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_new_dict</span>(<span class="hljs-params">self</span>) -&gt; Dict[int, str]:</span>
        <span class="hljs-keyword">raise</span> NotImplementedError

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_filtered_dict</span>(<span class="hljs-params">self, data: Dict[int, str]</span>) -&gt; Dict[int, str]:</span>
        <span class="hljs-keyword">raise</span> NotImplementedError

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_new_set</span>(<span class="hljs-params">self</span>) -&gt; Set[int]:</span>
        <span class="hljs-keyword">raise</span> NotImplementedError

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_filtered_set</span>(<span class="hljs-params">self, data: Set[int]</span>) -&gt; Set[int]:</span>
        <span class="hljs-keyword">raise</span> NotImplementedError

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_new_list</span>(<span class="hljs-params">self</span>) -&gt; List[int]:</span>
        <span class="hljs-keyword">raise</span> NotImplementedError

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_filtered_list</span>(<span class="hljs-params">self, data: List[int]</span>) -&gt; List[int]:</span>
        <span class="hljs-keyword">raise</span> NotImplementedError
</code></pre>
<p>I write here a part of code, example only for <code>dict</code>. Other listings you can find in <a target="_blank" href="https://github.com/z0rr0/blog/blob/main/posts/comprehensions-vs-cycles/comprehensions-vs-cycles.py">github</a>:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CycleBench</span>(<span class="hljs-params">BaseBench</span>):</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_new_dict</span>(<span class="hljs-params">self</span>) -&gt; Dict[int, str]:</span>
        result = {}
        <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(self.n):
            <span class="hljs-keyword">if</span> i % <span class="hljs-number">2</span>:
                result[i] = <span class="hljs-string">'odd'</span>
            <span class="hljs-keyword">else</span>:
                result[i] = <span class="hljs-string">'even'</span>
        <span class="hljs-keyword">return</span> result

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_filtered_dict</span>(<span class="hljs-params">self, data: Dict[int, str]</span>) -&gt; Dict[int, str]:</span>
        result = {}
        <span class="hljs-keyword">for</span> key, value <span class="hljs-keyword">in</span> data.items():
            <span class="hljs-keyword">if</span> key % <span class="hljs-number">2</span>:
                result[key] = value
        <span class="hljs-keyword">return</span> result


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ComprehensionBench</span>(<span class="hljs-params">BaseBench</span>):</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_new_dict</span>(<span class="hljs-params">self</span>) -&gt; Dict[int, str]:</span>
        <span class="hljs-keyword">return</span> {i: <span class="hljs-string">'odd'</span> <span class="hljs-keyword">if</span> i % <span class="hljs-number">2</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'even'</span> <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(self.n)}

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_filtered_dict</span>(<span class="hljs-params">self, data: Dict[int, str]</span>) -&gt; Dict[int, str]:</span>
        <span class="hljs-keyword">return</span> {key: value <span class="hljs-keyword">for</span> key, value <span class="hljs-keyword">in</span> data.items() <span class="hljs-keyword">if</span> key % <span class="hljs-number">2</span>}
</code></pre>
<p>It’s interesting to check comprehension efficiency with different sizes of collections, small [10-1000] and big [10k-100k].</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630440985004/lPtbPxeC0.png" alt="comprehension_vs_cycle_small.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630440999006/7ZC-kzC7y.png" alt="comprehension_vs_cycle_big.png" /></p>
<p>We see that more efficiency of comprehension is for <code>list</code>, and there is minimal time difference for <code>dict</code>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Yes, comprehension is faster and often does code more readable. But it is not a rule «always use comprehension». For some cases cycles are better. For example sometimes maintainability may be more important than performance.</p>
]]></content:encoded></item></channel></rss>