1、前言
从最开始的研究平衡三进制这东西,好像是过了几年了,它的逻辑门说实话巧妙是巧妙的,但是有时候复杂也是复杂的,为了更加方便的研究它,我用Rust语言来写了一个基础库,现在它终于有了质的突破,变的更方便使用了,以下库地址:https://crates.io/crates/ternary_arithmetic
2、实现思路
它有一个很好的编码,即天平编码T/0/1对应二进制的10/00/01,主要的特点就是对称,从最开始的时候,我是模仿别人的库采用的数组方式表示平衡三进制数,模仿当然不是照抄,它的封装方式很好,但它的运算就很离谱了,是将平衡三进制转成十进制交给二进制运算,而我用的是数组逻辑门,采用LUT查表法的方式来运算的,当时加/减/乘都很好写,直到后面的除法那时我是真的头大,还好那时找到解决办法,即二重商法。
这个二重商我感觉也挺神奇的,同一个位置可以上商再次,这个商是同样的符号,比如:++/+-,得到就是两个++是它的双倍,然后又到了后面感觉数组的方式太耗性能了,而1个u8才能表示一个平衡三进制数,当时就想能不能以字节为单位,一个u8就是有8个bit,那就可以装下4个trit,这就是2bit圧缩方案,我采用的也是这种,因为我学会了掩码操作这样就可以单独操作2个bit,还有一种方案也是8bit,用的是i8可以装下5trit的,也就直接用十进制来表示,比如:T11直接就是-5然后用二进制存起来,但解析很麻烦,但同样优点就是不用平衡三进制逻辑门,用二进制就能算,所以这种方式模拟的不彻底,我想要的是可以移植到fpga的模拟方案,后面的实现证明这方向是对的。
当我从数组方案向2bit转时,刚开始的是解决逻辑门问题,一开始用的还是数组,只不过是分成了四列,后面直接用到了位运算实现了并行计算,其间我最感觉麻烦的数清楚我输入了多少个0与多少个1,会不会输错了,终于有一天我真是忍不了,就写了个macro库最终解决输入的问题,也就是我输入10TT在编译时机器自动给我转成u801001010,输出就用标准库的fmt,终于是解决了输入输出不好用的问题,然后就是模拟平衡三进制电路设计的实现思路,写成了电路版本的代码,比如下面的非门实现,它其实就是交换了位置:
pub fn tneg(&self) -> Self { let val = self.0; let res=((val & 0xAA) >> 1) | ((val & 0x55) << 1); Trit4(res) }3、实现
计算机只会加法,一切运算都是加法。而这个版本我尝试用超前进位加法,但是模拟来模拟去,感觉这并不会给硬件提速,而实现超来也更复杂速度也就慢了,准确性也不太能保证,所以即使我写出了类超前进位加法的方法,用的还是三维数组实现的平衡三进制加法器,速度也是可以的,用这个我实现了加/减/乘/除的实现。
其中减法最简单,只要简单的取反被减数后相加就可以了,而乘法也是很简单的,通过移位操作与相加或相减后就可以得结果位数是2N位,除法是复杂一些,它要先用移位操作将被除数对齐除数,然后根据被除数与除数上商值,然后进行一轮减法或加法,被除数最高位不为0,则要第二轮减法,符合被除数则右移一位,商值左移一们进行下一轮的运算,最后得到答案,若除不尽的放在余数那,最后这样就实现了一个好用的基础库了,部分示例代码:
use trit_macro::trits; use ternary_arithmetic::trit::{Trit4,Trit8,Trit16,Trit32}; fn main() { // let a = trits!("T010_T010_T010_T010_T010_T010_T010_T010"); // let b = trits!("---0_---0_---0_---0_---0_---0_---0_---0"); // let c = trits!("0000_0000_0000_0000_0000_0000_0000_0000"); // let d = trits!("+++0_+++0_+++0_+++0_+++0_+++0_+++0_+++0"); let a = trits!("T010_T010_T010_T010"); let b = trits!("---0_---0_---0_---0"); let c = trits!("0000_0000_0000_0000"); let d = trits!("+++0_+++0_+++0_+++0"); // let a = trits!("T010_T010"); // let b = trits!("---0_---0"); // let c = trits!("0000_0000"); // let d = trits!("+++0_+++0"); // let a = trits!("T010"); // let b = trits!("---0"); // let c = trits!("0000"); // let d = trits!("+++0"); let result1 =a.tncons(b); let result2 =a.tncons(c); let result3 =a.tncons(d); println!("{}",result1); println!("{}",result2); println!("{}",result3); }整数乘法:结果是双倍位数。
整数除法: 如果两个操作数都是整数类型(例如 i32 或 u32),Rust 会执行整数除法。这意味着除法的结果会舍去小数部分(向零舍入),即只保留整数部。
整数取模: 如果操作数是整数类型(例如 i32 或 u32),Rust 会执行整数取模运算,并返回余数部分。
1TT / 1T =商10,余T。//5/2
T11 / T1 =商10,余1。//-5/-2
1TT / T1 =商T0,余T。//5/-2
T11 / 1T =商T0,余1。//-5/2
被除数 * 商 + 余数=除数
在这个例子中,5 除以 2 的商是 3,余数是 -1;而5 除以 3 的商是 2,余数是 -1。