为什么Rust中没有任意大小的二进制整数类型?

时间:2017-02-14 17:46:30

标签: types binary rust

Rust有二进制文字,二进制格式化程序和一组整数类型,但没有显式的二进制数字类型。

'几乎是二进制'整数

确实,无符号整数的预期实现是通用机器中的大/小端二进制数。但是,这与高级语言的语法相去甚远。例如,如果我有一个8位二进制数0000 0101,我想在句法上将其视为原始数字类型,我有两个问题:(1)数字的字符表示,以及(2)类型声明号码。如果我决定坚持u8,我必须添加一层字符串操作(在Rust中)或一层向量操作(例如in MATLAB),其中数字将按字面显示或声明,我必须确保二进制表示在u8中转换为它的等价物。在这种情况下,没有办法直接声明0000 0101 + 0000 0111没有这个机制冒泡到语法级别,而这只是二进制类型的大小恰好与整数类型对齐。

'真'二进制类型

例如,假设类型b3是一个3位二进制数,支持其字段中的相应数学运算。当然,这些操作至少是算术运算,关闭类型b3。 (定义类型的那个必须定义关于如何在实践中实现闭包的约定,例如,通过包装或断言不能在b3中表达的操作的结果。)

像这样的二进制类型可以声明为这样,然后在语法上使用与任何其他数字类型相同的方式。因此,101 + 001 == 110,无需部署按位运算符,以及其他增加的要求。

引擎盖下

如果这些操作在预期在其基础上具有二进制表示的编程语言中看起来很平淡,请注意implementing finite field arithmetic in C-like languages中存在细微之处:

/* Multiply two numbers in the GF(2^8) finite field defined 
 * by the polynomial x^8 + x^4 + x^3 + x + 1 = 0
 * using the Russian Peasant Multiplication algorithm
 * (the other way being to do carry-less multiplication followed by a modular reduction)
 */
uint8_t gmul(uint8_t a, uint8_t b) {
    uint8_t p = 0; /* the product of the multiplication */
    while (b) {
        if (b & 1) /* if b is odd, then add the corresponding a to p (final product = sum of all a's corresponding to odd b's) */
            p ^= a; /* since we're in GF(2^m), addition is an XOR */

        if (a & 0x80) /* GF modulo: if a >= 128, then it will overflow when shifted left, so reduce */
            a = (a << 1) ^ 0x11b; /* XOR with the primitive polynomial x^8 + x^4 + x^3 + x + 1 (0b1_0001_1011) – you can change it but it must be irreducible */
        else
            a <<= 1; /* equivalent to a*2 */
        b >>= 1; /* equivalent to b // 2 */
    }
    return p;
}

为什么要这么麻烦?

具有完成上述功能的特性实现的Rust类型会将所有这些内容折叠到Mul for b8,这在我看来是关于Rust的一个很棒的功能。能够使用比位掩码和移位更正式和标准的接口来引用b8数字的特征似乎也是Rust在这里提供的有用的东西。

为什么核心或板条箱中没有这种类型的原因是什么?

1 个答案:

答案 0 :(得分:2)

真诚地,也许所以我们都同意这里没有人是疯狂的(?),我implemented a crate这是尝试捕获Rust中有限字段的语义而不依赖于语言的基本期望或硬件。我必须警告你,它既没有经过严格的测试,也没有得到有效的实施,但它的编译也是如此。

它提供以下语义:

  • 如果您可以将有限域视为受限多项式的系数集或p-adic数的向量,则可以定义将系数存储为嘎嘎的向量的类型像一个数字。例如,可以使用以下宏生成两位二进制数字段:

    #![allow(non_camel_case_types)]
    #[macro_use] extern crate finite_fields;
    
    binary_type! { b2, 2 }
    

    该宏扩展为带有数组的newtype结构的实现:

    /// A binary number ($fieldwidth digits).
    #[derive(Clone, Copy, PartialEq)]
    pub struct $tyname([b1; $fieldwidth]);
    
    impl $tyname {
      pub fn new(vals: [b1; $fieldwidth]) -> $tyname {
        $tyname(vals)
      }
    } // ...
    
  • 定义的类型允许通常的算术运算在饱和时出现溢出错误并除以零错误。具体来说,我实施了OrderingAddSubMulDivBitXorIndex和{{1 on&#34; unit&#34;在宏中使用n-ary类型,然后将它们用作较大宏生成的n-ary数字的数字。

    IndexMut
  • 可以定义任何arity的有限字段,但是用户必须指定存储类型以满足Rust使用的标准类型:

    /// Arithmetic addition with overflow error.
    impl Add for $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 as $storsize))
        }
      }
    }
    

    这是我困惑的地方。这个crate的实现所显示的答案是肯定的,你可以将任意有限域语义提升到&#34; natural&#34; (即,base-10,base-2,base-8和base-16)数字字段嵌入到语言和硬件中(即,您可以假装它们是常规数字类型并获得您期望的Rustic检查,如果您认为newtypes是类型),但您仍然需要以存储开销的形式支付吹笛者(并且可能无法计算无效率)。我不会因为离散数学和应用CS之间的本体断层线而陷入困境,但我不确定它是否重要。

  • 无论如何,你可以使用相同的基本宏来完成愚蠢的事情,比如在base-7中工作:

    /// Creates a ternary type named `t2`, with a unit type named `t1`, storing each
    /// digit in a `u8`, with two digits.
    nary_type! { t2, t1, 3, u8, 2 }
    

万岁。让我们全都喝醉,忘记发生的一切。