/08
Compiler
C++ you can edit and run in the page. Change the code, press Run, and it is rebuilt on Compiler Explorer: output, exit code, assembly.
Run it
Shannon entropy in one pass: count each byte value, then sum −p·log₂p over the buckets. Edit a string and run it again. One repeated byte carries no information, two symbols in equal measure carry one bit each, eight distinct bytes carry three.
#include <cmath>
#include <cstdio>
#include <string_view>
// Shannon entropy in bits per byte: one counting pass, then sum -p log2 p.
double entropy(std::string_view s) {
unsigned long long counts[256] = {};
for (unsigned char c : s) counts[c]++;
double h = 0;
for (auto c : counts)
if (c) { double p = double(c) / s.size(); h -= p * std::log2(p); }
return h;
}
int main() {
for (std::string_view s : {"aaaaaaaa", "abababab", "abcdefgh",
"the quick brown fox jumps over the lazy dog"})
std::printf("%6.3f bits/byte %.*s\n", entropy(s), int(s.size()), s.data());
} 0.000 bits/byte aaaaaaaa
1.000 bits/byte abababab
3.000 bits/byte abcdefgh
4.385 bits/byte the quick brown fox jumps over the lazy dog Read the machine code
The same counting loop compiled twice. On x86-64 it is five instructions: movzx
the byte, then add straight into memory. For WebAssembly, clang unrolls it by
four and, on a stack machine, spells every increment out as i64.load,
i64.add, i64.store. Edit the loop and both targets recompile.
// The counting loop from fig.01: one increment per byte.
void feed(unsigned long long* counts, const unsigned char* p, unsigned n) {
for (unsigned i = 0; i < n; i++) counts[p[i]]++;
} feed(unsigned long long*, unsigned char const*, unsigned int):
test edx, edx
je .L1
mov edx, edx
add rdx, rsi
.L3:
movzx eax, BYTE PTR [rsi]
add rsi, 1
add QWORD PTR [rdi+rax*8], 1
cmp rsi, rdx
jne .L3
.L1:
ret feed(unsigned long long*, unsigned char const*, unsigned int):
block
local.get 2
i32.eqz
br_if 0
local.get 2
i32.const 3
i32.and
local.set 3
i32.const 0
local.set 4
block
local.get 2
i32.const 4
i32.lt_u
br_if 0
local.get 2
i32.const -4
i32.and
local.set 5
i32.const 0
local.set 4
loop
local.get 0
local.get 1
local.get 4
i32.add
local.tee 2
i32.load8_u 0
i32.const 3
i32.shl
i32.add
local.tee 6
local.get 6
i64.load 0
i64.const 1
i64.add
i64.store 0
local.get 0
local.get 2
i32.const 1
i32.add
i32.load8_u 0
i32.const 3
i32.shl
i32.add
local.tee 6
local.get 6
i64.load 0
i64.const 1
i64.add
i64.store 0
local.get 0
local.get 2
i32.const 2
i32.add
i32.load8_u 0
i32.const 3
i32.shl
i32.add
local.tee 6
local.get 6
i64.load 0
i64.const 1
i64.add
i64.store 0
local.get 0
local.get 2
i32.const 3
i32.add
i32.load8_u 0
i32.const 3
i32.shl
i32.add
local.tee 2
local.get 2
i64.load 0
i64.const 1
i64.add
i64.store 0
local.get 5
local.get 4
i32.const 4
i32.add
local.tee 4
i32.ne
br_if 0
end_loop
local.get 3
i32.eqz
br_if 1
end_block
local.get 1
local.get 4
i32.add
local.set 2
loop
local.get 0
local.get 2
i32.load8_u 0
i32.const 3
i32.shl
i32.add
local.tee 4
local.get 4
i64.load 0
i64.const 1
i64.add
i64.store 0
local.get 2
i32.const 1
i32.add
local.set 2
local.get 3
i32.const -1
i32.add
local.tee 3
br_if 0
end_loop
end_block
end_function
Code runs on Compiler Explorer, requested from your browser only when you press Run. Results shown before that were compiled when this page was built.