Source of base.plp

<(common.inc.plp)><:

Html({
	title => 'number bases',
	version => '1.2',
	description => [
		"Cheat sheets summarising various software programs and standards.",
	],
	keywords => [qw'
		sheet cheat reference software overview summary help keyboard map unicode
	'],
	stylesheet => [qw'light dark circus mono red'],
});

my @cols = (2, 6, 8, 9, 10, 12, 16, 18, 20);
my @morecols = (2 .. 6, 8, 9, 10, 12, 16, 18, 20, 24, 32, 36, 64);
my @char = (0..9, 'A'..'Z', 'a'..'z');
my %RADIXNAME = (
	 2 => 'binary',
	 3 => 'ternary',
	#4 => 'quaternary',
	#5 => 'quinary',
	 6 => 'senary',
	 8 => 'octal',
	#9 => 'nonary',
	10 => 'decimal',
	12 => 'dozenal', # duodecimal
	16 => 'hexadecimal',
	20 => 'vigesimal',
	#36 => 'double-senary',
	60 => 'sexagesimal',
	#64 => 'double-octal',
);
:>
<h1>Number bases</h1>

<h2>Radix economy</h2>
<table class=mapped>
<:
sub showcolhead {
	print '<col>';
	my @spans;
	$spans[ $_ > 10 ]++ for @_;
	print "<colgroup span=$_>" for @spans;
	print '<thead><tr><th>';
	for (@_) {
		print '<th>', $_ < 36 ? $char[$_] : $char[35].'+'.$char[$_ - 35];
		print " <small>($_)</small>" for join(', ',
			$RADIXNAME{$_} // (),
			$_ >= 10 ? "base$_" : (),
		) || ();
	}
	say '</thead>';
}

sub radix_economy {
	my ($val, $radix) = @_;
	return $radix * int(log($val) / log($radix) + 1);
}

use List::Util 'sum';

showcolhead(@morecols);

for my $max (100, 255, 1024) {
	print '<tr><th>⍳', $max;
	for my $radix (@morecols) {
		printf '<td style="text-align:right">%.1f',
			sum(map { radix_economy($_, $radix) } 1 .. $max) / $max;
	}
}
:></table>

<h2>Reciprocal fractions (n⁻¹)</h2>
<table class=mapped>
<:
use Math::BigFloat;

my $count = 40;
my $places = $count<<1;

sub showfrac {
	my ($num, $radix) = @_;

	my $out = '';
	my $class = '';
	my $zeros = 0;

ADD_DIGITS:
	for my $place (1 .. $places) {
		# add a digit in requested base (left shift)
		$out .= $char[ $num->blsft(1, $radix) ];
		$num->bmod(1) or do {
			# no remaining fractional part
			$class = $out eq '1' ? 'l5' : $place == 1 ? 'l4' : 'l3';
			last;
		};
		$zeros++ if $out =~ /^0+$/;

		for my $check ($zeros .. length($out)>>1) {
			if (substr($out, -$check) eq substr($out, -$check*2, $check)) {
				$class = $check == 1 ? 'l2' : 'l1';
				substr($out, -$check) = '';
				substr($out, -$check, 0) = '<span style="text-decoration:overline">';
				$check .= '</span>';
				last ADD_DIGITS;
			}
		}
	}
	printf '<td%s style="text-align:left">%s', $class && qq( class="$class"), $out;
}

showcolhead(@cols);

for my $n (2 .. $count) {
	print '<tbody>' if $n % 8 == 1;
	print '<tr>';
	print '<th>', $n;
	for my $radix (@cols) {
		my $accuracy = int($places * log($radix) / log(10));
		Math::BigFloat->accuracy($accuracy);
		showfrac(scalar Math::BigFloat->new(1)->bdiv($n, $accuracy+1), $radix);
	}
	say '';
}

:></table>

<hr>

<h2>Duplication (2ⁿ)</h2>
<table class=mapped>
<:
sub showint {
	my ($int, $radix) = @_;
	my @digits;
	while ($int >= 1) {
		push @digits, $char[$int % $radix];
		$int /= $radix;
	}
	splice @digits, 3 * $_, 0, '&nbsp;' for reverse 1 .. @digits/3;
	return join '', reverse @digits;
}

@cols = grep { not $_ ~~ [2,8,16] } @cols, 36;
showcolhead(@cols);

for my $n (0, 3 .. 16, 0, 18, 20, 24, 30, 32, 36, 40, 48, 50, 60, 64) {
	if (!$n) {
		print '<tbody>';
		next;
	}
	print '<tr>';
	print '<th>', $n;
	for my $radix (@cols) {
		print '<td style="text-align:right">', showint(2 ** $n, $radix);
	}
	say '<th>', {
		 4 => 'nibble',
		 8 => 'octet',
		16 => '2o',
		24 => '3o',
		32 => '4o',
		40 => '5o, Tebi',
		48 => '6o',
		64 => 'o²',
		10 => 'kibi',
		20 => 'Mebi',
		30 => 'Gibi',
		50 => 'Pebi',
		60 => 'Exbi',
		70 => 'Zebi',
		80 => 'Yobi',
	}->{$n} // '';
}

:></table>