John McFarlane
johnmcfarlane.github.io/slides/2022-cppindiaSoftware Engineer, Jaguar Land Rover, Shannon, Ireland
BSI, WG21 (SG6, SG14, SG21)
BBC BASIC, 6502, C, 80286/7, C++, Python
Games, Fractals, AI, Simulation, APIs, Numerics, CI
…0.0…
^
Question:
how many digits does uint8_t
have?
Answer:
…00000000000101010
=
32 + 8 + 2
…00000000000101010
=
42
000000…
…00000000000000101010.00000000000000…
…00000000000000101010.00000000000000…
…00000000000000101010.00000000000000…
Question:
how many digits does uint8_t
have?
Answer:
∞+8+∞ = 2∞+8
Question:
how many sign bits in int8_t
?
Answer:
∞+1
…1111111111111010110
=
-128+64+16+4+2
…1111111111111010110
=
-420000000000…
…1111111111111010110.000000000000000…
…1111111111111010110.000000000000000…
…1111111111111010110.000000000000000…
…1111111111111010110.000000000000000…
Question:
how many sign bits in int8_t
?
Answer:
∞+1
π
?
…0000011.001001000011111101101010100…
…0000011.001001000011111101101010100…
…0000011.001001000011111101101010100…
π
?
float
?
…0000011.001001000011111101101010100…
…0000011.001001000011111101101010100…
…0000011.001001000011111101101010100…
float
?
Answer:
3
…00000000000101010.000000…
✓
…00000000000101010.000000…
✓
…00000000000101010.000000…
✓
…00000000000101010.000000…
✓
…00000000000101010.000000…
✓
…00000000000101010.000000…
✓
…00000000000101010.000000…
✓
…00000000000101010.000000…
✓
…00000000000101010.000000…
✓
…00000000000101010.000000…
✓
…00000000000101010.000000…
✓
…00000000000101010.000000…
✓
Answer:
3
-std=c++20
#include <cnl/all.h>
using namespace cnl;
using namespace cnl::literals;
using namespace fmt;
using namespace std;
using namespace std::math;
<O,0>
-
Question:
how do you write 'forty-two'?
Answer:
scaled_integer{42_c}
github.com/johnmcfarlane/cnl/tree/v1.x
template<int Exponent=0, int Radix=2>
class power;
template<typename Rep=int, typename Scale=power<>>
class scaled_integer;
template<typename Rep, int Exponent, int Radix>
class scaled_integer<Rep, power<Exponent, Radix>> {
// ...
private:
Rep n;
};
value = n×RadixExponent
42... it's a byte, right?
…00000000000101010.000000…
✓
int8_t{42}
scaled_integer<int8_t, power<1>>{42} // 21×21
scaled_integer<int, power<1>>{42}
CNL's zero-cost abstraction
auto a(float value)
-> scaled_integer<int8_t, power<1>>
{
return value;
}
a(float):
mulss xmm0, DWORD PTR .LC0[rip]
cvttss2si eax, xmm0
ret
auto b(float value)
-> int8_t
{
return value * .5f;
}
b(float):
mulss xmm0, DWORD PTR .LC0[rip]
cvttss2si eax, xmm0
ret
int
's zero-cost abstraction
auto a(float value)
-> scaled_integer<int , power<1>>
{
return value;
}
a(float):
mulss xmm0, DWORD PTR .LC0[rip]
cvttss2si eax, xmm0
ret
auto b(float value)
-> int
{
return value * .5f;
}
b(float):
mulss xmm0, DWORD PTR .LC0[rip]
cvttss2si eax, xmm0
ret
std::int8_t
costly abstraction!
auto a(float value) -> int
{
return value * .5f;
}
to_int8(float):
vmov.f32 s15, #5.0e-1
vmul.f32 s0, s0, s15
vcvt.s32.f32 s15, s0
vmov r0, s15 @ int
bx lr
auto b(float value) -> int8_t
{
return value * .5f;
}
to_int(float):
sub sp, sp, #8
vmov.f32 s15, #5.0e-1
vmul.f32 s0, s0, s15
vcvt.s32.f32 s15, s0
vstr.32 s15, [sp, #4] @ int
ldrsb r0, [sp, #4]
add sp, sp, #8
bx lr
42... it's a byte, right?
…00000000000101010.000000…
✓
int8_t{42}
scaled_integer<int8_t, power<1>>{42}
scaled_integer<int, power<1>>{42}
namespace cnl::literals {
template<char... Chars>
constexpr auto operator ""_c() noexcept;
}
// constant<42>
auto forty_two = 42_c;
// scaled_integer<int, power<1>>{42}
auto a = scaled_integer{forty_two};
godbolt.org/z/daYsW48j6
int8_t{42}
scaled_integer<int8_t, power<1>>{42}
scaled_integer<int, power<1>>{42}
scaled_integer{42_c}
Answer:
scaled_integer{42_c}
Question:
how many bits in a megabyte?
Answer:
1M
1000×1000
1111101000×1111101000
=11110100001001000000
1M
1000×1000
1111101000×1111101000
=11110100001001000000
1M
1000×1000
1111101000×1111101000
=11110100001001000000
1M
1000×1000
1111101000×1111101000
=11110100001001000000
// scaled_integer<int, power<3>>{1000}
auto k = scaled_integer{1000_c};
// scaled_integer<int, power<6>>{1000000}
auto m = k*k;
godbolt.org/z/46Mb6YdTe
1M
1000×1000
=1000000
1M
1000×1000
=1000000
1M
1000×1000
=1000000
Decimal Fixed-Point
// scaled_integer<int, power<3, 10>>{1000}
auto k = scaled_integer<int, power<3, 10>>{1000};
// scaled_integer<int, power<6, 10>>{1000000}
auto m = k*k;
Question:
how far does the queen really move in Chess?
1.414213562373095048801688724209698078569671875…
…001.01101010000010011110011001100111111100111011…
sqrt2_v<scaled_integer<int, power<-30>>>
(5.8284270763397216796875)
wandbox.org/permlink/GBY1czkvkZ8jytlSusing Distance = scaled_integer<int, power<-24>>;
Distance queens_distance(int columns, int rows)
{
columns = std::abs(columns);
rows = std::abs(rows);
int diagonal = std::min(columns, rows);
int axial = std::max(columns, rows)-diagonal;
return sqrt2_v<Distance>*diagonal+axial;
}
godbolt.org/z/abGvsb9TW
queens_distance(int, int):
mov eax, edi
neg eax
cmovs eax, edi
mov edx, esi
neg edx
cmovs edx, esi
cmp eax, edx
mov ecx, edx
cmovle ecx, eax
cmovl eax, edx
sub eax, ecx
sal eax, 24
imul ecx, ecx, 23726566
add eax, ecx
ret
Question:
how far does the queen really move in Chess?
(0/0)
o
…00001.0110101000001001111001100110011111110…
auto root2 = sqrt2_v<scaled_integer<int, power<-30>>>;
…00001.0110101000001001111001100110010000000…
auto root32 = root2 * 4;
…00101.1010100000100111100110011001000000000…
<source>:7:33: error: overflow in constant expression [-fpermissive]
7 | constexpr auto root32 = root2 * 4;
| ^
…00001.0110101000001001111001100110011111110…
constexpr auto root2 = sqrt2_v<scaled_integer<int, power<-30>>>;
…00001.0110101000001001111001100110010000000…
constexpr auto root32 = root2 * 4;
…0101.1010100000100111100110011001000000000…
<source>:7:33: error: overflow in constant expression [-fpermissive]
7 | constexpr auto root32 = root2 * 4;
| ^
If you remember one slide...
c++ -std=c++20 -fsanitize=address,undefined -fno-sanitize-recover=all
-Wall -Wextra -Werror all_your_source_files.cpp
auto root2 = sqrt2_v<scaled_integer<int, power<-30>>>;
auto root32 = root2 * 4;
Program returned: 1
/opt/compiler-explorer/libs/cnl/v1.x/include/cnl/_impl/operators/operators.h:76:28:
runtime error: signed integer overflow:1518500249 * 4 cannot be represented in type 'int'
godbolt.org/z/b6nY6ohWd
unsigned mu = 0;
fmt::print("{}\n", mu-1);
4294967295
int s = 1;
fmt::print("{}\n", s << 31);
-2147483648
int ms = 100000;
fmt::print("{}\n", short(ms));
-31072
// cnl/overflow_integer.h
namespace cnl {
template<
typename Rep,
class OverflowTag=undefined_overflow_tag>
class overflow_integer;
}
overflow_integer<unsigned> mu = 0;
cout << (mu-1);
runtime error: execution reached an unreachable program point
overflow_integer<int> s = 1;
cout << (s<<31);
runtime error: execution reached an unreachable program point
overflow_integer<int> ms = 100000;
cout << short(ms);
runtime error: execution reached an unreachable program point
overflow_integer<unsigned> mu = 0;
fmt::print("{}\n", mu-1);
negative overflow
overflow_integer<int> s = 1;
fmt::print("{}\n", s << 31);
positive overflow
overflow_integer<int> ms = 100000;
fmt::print("{}\n", short(ms));
positive overflow
// cnl/elastic_integer.h
namespace cnl {
template<int Digits, typename Narrowest=int>
class elastic_integer;
}
11111010004
=111101000010010000002
=1110100011010100101001010001000000000000
// elastic_integer<10>{1000}
auto k = elastic_integer{1000_c};
// elastic_integer<20>{1000000}
auto m = k*k;
// elastic_integer<40>{1000000000000LL}
auto t = m*m;
godbolt.org/z/xonn6nxYf
// cnl/elastic_integer.h
namespace cnl {
template<int Digits, typename Narrowest=int>
class elastic_integer;
}
11111010004
=111101000010010000002
=1110100011010100101001010001000000000000
// elastic_integer<10>{1000}
auto k = elastic_integer{1000_c};
// elastic_integer<20>{1000000}
auto m = k*k;
// elastic_integer<40>{1000000000000LL}
auto t = m*m;
godbolt.org/z/WKEkRk
// cnl/elastic_scaled_integer.h
namespace cnl {
template<int Digits, int Exponent>
using elastic_scaled_integer =
scaled_integer<elastic_integer<Digits>, power<Exponent>>;
namespace literals {
template<char ... Chars>
constexpr auto operator ""_elastic() noexcept;
}
}
// user code
using namespace cnl::literals;
// elastic_scaled_integer<7, 3>
constexpr auto k = 1000_elastic;
godbolt.org/z/Yj5YG5W1e
// cnl/elastic_integer.h
namespace cnl {
template<int Digits, typename Narrowest=int>
class elastic_integer;
}
11111010004
=111101000010010000002
=1110100011010100101001010001000000000000
// elastic_integer<10>{1000}
auto k = elastic_integer{1000_c};
// elastic_integer<20>{1000000}
auto m = k*k;
// elastic_integer<40>{1000000000000LL}
auto t = m*m;
godbolt.org/z/G4bEvnoEs
// cnl/elastic_scaled_integer.h
namespace cnl {
template<int Digits, int Exponent>
using elastic_scaled_integer = ...;
}
11111010004
=111101000010010000002
=1110100011010100101001010001000000000000
// elastic_scaled_integer<7, 3>{1000}
auto k = 1000_elastic;
// elastic_scaled_integer<14, 6>{1000000}
auto m = k*k;
// elastic_scaled_integer<28, 12>{1000000000000LL}
auto t = m*m;
godbolt.org/z/ncc4vbT4h
auto root2 = sqrt2_v<scaled_integer<int, power<-30>>>;
…00001.0110101000001001111001100110010000000…
// scaled_integer<int, power<-28>>
auto root32 = root2 << 2_c;
…0000101.10101000001001111001100110010000000…
// elastic_scaled_integer<31, -28>
auto root32 = root2 * 4_elastic;
…0000101.10101000001001111001100110010000000…
auto f(scaled_integer<int, power<-30>> n) {
return n << 2_c;
}
f:
mov eax, edi
auto g(scaled_integer<int, power<-30>> n) {
return n * 4_elastic;
}
g:
mov eax, edi
2∞+8
∞+1
3
scaled_integer{42_c}