Why is there no arbitrary-sized binary integer type in Rust? -
rust has binary literals, binary formatter, , host of integer types, no explicit binary numeric type.
'almost-binary' integers
it's true expected implementation of unsigned integer big/little-endian binary number in general purpose machines. however, that's far removed syntax of high-level language. example, if have eight-bit binary number 0000 0101
want treat syntactically primitive numeric type, have 2 problems: (1) character representation of number, , (2) type declaration number. if decide stick u8
, have add layer of string operations (in rust) or layer of vector operations (in matlab, example) numbers displayed or declared literally, , have ensure binary representation converted equivalent in u8
. in situation, there's no way make direct statement 0000 0101 + 0000 0111
without machinery bubbling syntactic level, , that's binary types sizes happen line integer types.
a 'true' binary type
an example be, say, hypothetical type b3
, 3-bit binary number, supporting appropriate mathematical operations in field. @ minimum, operations arithmetic, closed on type b3
, of course. (the 1 defining type have define convention how closure achieved in practice, e.g., wrapping or asserting result of operation can't expressed in b3
not defined.)
a binary type declared such , used syntactically same way other numeric type. thus, 101 + 001 == 110
, without need deploy bitwise operators, among other added requirements.
under hood
if these operations seem prosaic in programming language expected have binary representations @ foundation, note there subtleties in implementing finite field arithmetic in c-like languages:
/* multiply 2 numbers in gf(2^8) finite field defined * polynomial x^8 + x^4 + x^3 + x + 1 = 0 * using russian peasant multiplication algorithm * (the other way being carry-less multiplication followed modular reduction) */ uint8_t gmul(uint8_t a, uint8_t b) { uint8_t p = 0; /* product of multiplication */ while (b) { if (b & 1) /* if b odd, add corresponding p (final product = sum of a's corresponding odd b's) */ p ^= a; /* since we're in gf(2^m), addition xor */ if (a & 0x80) /* gf modulo: if >= 128, overflow when shifted left, reduce */ = (a << 1) ^ 0x11b; /* xor primitive polynomial x^8 + x^4 + x^3 + x + 1 (0b1_0001_1011) – can change must irreducible */ else <<= 1; /* equivalent a*2 */ b >>= 1; /* equivalent b // 2 */ } return p; }
why bother?
a rust type trait implementations accomplishing above collapse of down mul b8
, seems me great feature rust. being able refer characteristics of b8
number using more formal , standard interface bitmasks , shifts seem useful thing rust offer here.
what reasons why no such types present anywhere in core or crates?
in faith, , perhaps can agree nobody here crazy (?), implemented crate attempt @ capturing semantics of finite fields in rust independent of underlying expectations of language or hardware. have warn neither rigorously tested nor efficiently implemented, compiles , examples.
it offers following semantics:
if can think of finite field either set of coefficients of restricted polynomial, or vector of p-adic numbers, can define type store coefficients vector quacks number. example, field of two-digit binary numbers can generated following macro:
#![allow(non_camel_case_types)] #[macro_use] extern crate finite_fields; binary_type! { b2, 2 }
that macro expands implementation of newtype struct carrying array:
/// binary number ($fieldwidth digits). #[derive(clone, copy, partialeq)] pub struct $tyname([b1; $fieldwidth]); impl $tyname { pub fn new(vals: [b1; $fieldwidth]) -> $tyname { $tyname(vals) } } // ...
the types defined admit usual arithmetic operations overflow errors on saturation , divide 0 errors. specifically, implemented
ordering
,add
,sub
,mul
,div
,bitxor
,index
, ,indexmut
on "unit" n-ary types in macros, , used digits of larger macro-generated n-ary numbers./// arithmetic addition overflow error. impl add $tyname { type output = result<$tyname, overflowerror>; fn add(self, other: $tyname) -> result<$tyname, overflowerror> { let sum = self.0 + other.0; if sum > $arity - 1 { err(overflowerror::default { arg1: self.to_string(), arg2: other.to_string() }) } else { ok($tyname(sum $storsize)) } } }
finite fields of arity can defined, storage type has specified user meet standard types used rust:
/// creates ternary type named `t2`, unit type named `t1`, storing each /// digit in `u8`, 2 digits. nary_type! { t2, t1, 3, u8, 2 }
this locus of confusion. answer shown crate's implementation yes, can raise arbitrary finite field semantics level of "natural" (i.e., base-10, base-2, base-8, , base-16) numeric fields embedded language , hardware (i.e., can pretend they're regular numeric types , rustic checks expect, if believe newtypes types), still need pay piper in form of storage overhead (and incurable calculation inefficiency). don't think i'm alone in being caught short ontological fault line between discrete math , applied cs, i'm not sure matters more.
at rate, can totally goofy things same basic macros, work in base-7:
/// creates septary type named `s3`, unit type named `s1`, each digit /// stored in `u8`, 3 digits. nary_type! { s3, s1, 7, u8, 3 }
hooray. let's drunk , forget whole thing happened.
Comments
Post a Comment