From 6c5c27cea04c604ae8eef1ab33cd1991372a7d05 Mon Sep 17 00:00:00 2001 From: lrlna Date: Sun, 19 Jul 2020 19:14:08 +0200 Subject: [PATCH 1/5] use latest version of bitvec --- Cargo.lock | 28 +++++++++--- Cargo.toml | 12 ++--- src/exponent.rs | 38 +++++++++++++++ src/lib.rs | 112 ++++++--------------------------------------- src/significand.rs | 58 +++++++++++++++++++++++ tests/test.rs | 2 +- 6 files changed, 140 insertions(+), 110 deletions(-) create mode 100644 src/exponent.rs create mode 100644 src/significand.rs diff --git a/Cargo.lock b/Cargo.lock index 00a5af6..ea4debb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,12 +29,16 @@ dependencies = [ [[package]] name = "bitvec" -version = "0.10.1" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "radium 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "byteorder" -version = "1.3.1" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -51,11 +55,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "decimal128" version = "0.1.0" dependencies = [ - "bitvec 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitvec 0.17.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "failure" version = "0.1.5" @@ -97,6 +106,11 @@ dependencies = [ "proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "radium" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rustc-demangle" version = "0.1.14" @@ -151,15 +165,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" "checksum backtrace 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f106c02a3604afcdc0df5d36cc47b44b55917dbaf3d808f71c163a0ddba64637" "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" -"checksum bitvec 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a75bc479a30a91415adcfbd39f17cb2255495b39f7b1e25e998e48b0363c7a6" -"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" +"checksum bitvec 0.17.4 (registry+https://github.com/rust-lang/crates.io-index)" = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" +"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" "checksum cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5f3fee5eeb60324c2781f1e41286bdee933850fff9b3c672587fed5ec58c83" "checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" "checksum proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)" = "ba92c84f814b3f9a44c5cfca7d2ad77fa10710867d2bbb1b3d175ab5f47daa12" "checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" +"checksum radium 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" "checksum rustc-demangle 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ccc78bfd5acd7bf3e89cffcf899e5cb1a52d6fafa8dec2739ad70c9577a57288" "checksum syn 0.15.32 (registry+https://github.com/rust-lang/crates.io-index)" = "846620ec526c1599c070eff393bfeeeb88a93afa2513fc3b49f1fea84cf7b0ed" "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" diff --git a/Cargo.toml b/Cargo.toml index 2e447b9..a7c47bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "decimal128" -version = "0.1.0" -license = "Apache-2.0" -description = "128-bit wide floating point implementation for Rust" authors = ["lrlna "] +description = "128-bit wide floating point implementation for Rust" edition = "2018" +license = "Apache-2.0" +name = "decimal128" +version = "0.1.0" [dependencies] +bitvec = "0.17.4" +byteorder = "1.3.4" failure = "0.1.2" -byteorder = "1.2.1" -bitvec = "0.10.1" diff --git a/src/exponent.rs b/src/exponent.rs new file mode 100644 index 0000000..985ebe4 --- /dev/null +++ b/src/exponent.rs @@ -0,0 +1,38 @@ +use bitvec::prelude::*; +use byteorder::{ByteOrder, BigEndian, ReadBytesExt}; +use std::io::Cursor; + +#[derive(Clone, PartialEq, PartialOrd)] +// where msb0 is big endian. +pub struct Exponent { + vec: BitVec, +} +/// Exponent is a 14-bit portion of decimal128 that follows the sign bit. Here we +/// are storing it as a 16-bit BitVec that can be later converted to a u16. +impl Exponent { + pub fn new() -> Self { + Exponent { + vec: bitvec![Msb0, u8; 0; 2], + } + } + + pub fn append(&mut self, vec: &mut BitVec) { + self.vec.append(vec) + } + + pub fn is_zero(&self) -> bool { + self.to_num() == 0 + } + + pub fn to_num(&self) -> u16 { + let mut reader = Cursor::new(&self.vec); + reader.read_u16::().unwrap() + } + + // compare current exponent value with exponent bias (largest possible + // exponent value) + // TODO: check if 6176 (exponent bias) can be stored as u16 + pub fn to_adjusted(&self) -> i16 { + self.to_num() as i16 - 6176 as i16 + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 56eacb2..b8324a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,21 +2,16 @@ //! [1bits] [ 14bits ] [ 113 bits ] //! sign exponent significand //! field -use bitvec::{bitvec, BigEndian, BitVec}; -use byteorder::*; +use bitvec::prelude::*; use std::cmp::Ordering; use std::fmt; -use std::io::Cursor; use std::str::FromStr; -#[derive(Clone, PartialEq, PartialOrd)] -pub struct Exponent { - vec: BitVec, -} -#[derive(Clone, PartialEq, PartialOrd)] -pub struct Significand { - vec: BitVec, -} +mod significand; +mod exponent; + +pub use significand::Significand; +pub use exponent::Exponent; #[derive(Clone)] pub struct Decimal128 { @@ -134,9 +129,9 @@ impl Decimal128 { // in this case second byte of the buffer can just be // straight up appended to the exponent. let byte_2 = buffer[1]; - let mut sb_bv: BitVec = (&[byte_2] as &[u8]).into(); - total_exp.append(&mut sb_bv); - // out of the third byte the first bit are part of the + let mut second_byte_bitvec = BitVec::from_element(byte_2); + total_exp.append(&mut second_byte_bitvec); + // out of the third byte the first bit is part of the // exponent, and the last 7 bits are part of the significand let byte_3 = buffer[1]; let h = if (byte_2 | 0b0111_1111) == max { 1 } else { 0 }; @@ -188,8 +183,8 @@ impl Decimal128 { total_sig.append(&mut sig); // add the whole third byte to the signficand in this case let byte_3 = buffer[2]; - let mut tb_bv: BitVec = (&[byte_3] as &[u8]).into(); - total_sig.append(&mut tb_bv); + let mut third_byte_bitvec = BitVec::from_element(byte_3); + total_sig.append(&mut third_byte_bitvec); NumberType::Finite } }, @@ -197,8 +192,9 @@ impl Decimal128 { // the rest of the bytes of the vec we are passed in. for bytes in 3..buffer.len() { - let mut bv: BitVec = (&[buffer[bytes]] as &[u8]).into(); - total_sig.append(&mut bv); + let mut bitvec = BitVec::from_element(buffer[bytes]); + // let mut bv: BitVec = (&[buffer[bytes]] as &[u8]).into(); + total_sig.append(&mut bitvec); } let dec128 = match combination_field { @@ -494,87 +490,9 @@ impl fmt::LowerHex for Decimal128 { } } -/// Exponent is a 14-bit portion of decimal128 that follows the sign bit. Here we -/// are storing it as a 16-bit BitVec that can be later converted to a u16. -impl Exponent { - pub fn new() -> Self { - Exponent { - vec: bitvec![BigEndian, u8; 0; 2], - } - } - - pub fn append(&mut self, vec: &mut BitVec) { - self.vec.append(vec) - } - - pub fn is_zero(&self) -> bool { - self.to_num() == 0 - } - - pub fn to_num(&self) -> u16 { - let mut reader = Cursor::new(&self.vec); - reader.read_u16::().unwrap() - } - - // compare current exponent value with exponent bias (largest possible - // exponent value) - // TODO: check if 6176 (exponent bias) can be stored as u16 - pub fn to_adjusted(&self) -> i16 { - self.to_num() as i16 - 6176 as i16 - } -} - -/// Significand is a padded 111- or 113-bit coefficient. We are storing it as a -/// 128-bit BitVec with the padded difference. This can be converted to a u128. -impl Significand { - pub fn new() -> Self { - Significand { - vec: bitvec![BigEndian, u8; 0; 14], - } - } - - pub fn append(&mut self, vec: &mut BitVec) { - self.vec.append(vec) - } - - pub fn is_zero(&self) -> bool { - // FIXME: Very inefficient, but docs are down - self.count_digits() == 0 - } - - pub fn to_num(&self) -> u128 { - let mut reader = Cursor::new(&self.vec); - reader.read_u128::().unwrap() - } - - pub fn max_value() -> u128 { - u128::from_str_radix("9999999999999999999999999999999999", 10).unwrap() - } - - // count the number of digits in the significand. This method first converts - // significand BitVec into a u128 number, then converts it to string to - // count characters and collects them in a vec to look at the vec's length. - // - // We return a u16 number of digits, as it's easier to compare to the - // exponent since that's also stored as a u16. - fn count_digits(&self) -> i16 { - self.as_digit_vec().len() as i16 - } - - fn as_digit_vec(&self) -> Vec { - let digits: Vec = self - .to_num() - .to_string() - .chars() - .map(|c| c.to_digit(10).unwrap()) - .collect(); - return digits; - } -} - fn stringify_vec(vec: Vec) -> String { vec.into_iter() .map(|d| d.to_string()) .collect::>() .join("") -} +} \ No newline at end of file diff --git a/src/significand.rs b/src/significand.rs new file mode 100644 index 0000000..27340cd --- /dev/null +++ b/src/significand.rs @@ -0,0 +1,58 @@ +use bitvec::prelude::*; +use byteorder::{BigEndian}; +use std::io::Cursor; + +#[derive(Clone, PartialEq, PartialOrd)] +// where msb0 is big endian. +pub struct Significand { + vec: BitVec, +} + +/// Significand is a padded 111- or 113-bit coefficient. We are storing it as a +/// 128-bit BitVec with the padded difference. This can be converted to a u128. +impl Significand { + pub fn new() -> Self { + Significand { + vec: bitvec![Msb0, u8; 0; 14], + } + } + + pub fn append(&mut self, vec: &mut BitVec) { + self.vec.append(vec) + } + + pub fn is_zero(&self) -> bool { + // FIXME: Very inefficient, but docs are down + self.count_digits() == 0 + } + + pub fn to_num(&self) -> u128 { + let mut reader = Cursor::new(&self.vec); + // BigEndian::read_u128(&self.vec) + reader.read_u128::().unwrap() + } + + pub fn max_value() -> u128 { + u128::from_str_radix("9999999999999999999999999999999999", 10).unwrap() + } + + // count the number of digits in the significand. This method first converts + // significand BitVec into a u128 number, then converts it to string to + // count characters and collects them in a vec to look at the vec's length. + // + // We return a u16 number of digits, as it's easier to compare to the + // exponent since that's also stored as a u16. + pub fn count_digits(&self) -> i16 { + self.as_digit_vec().len() as i16 + } + + pub fn as_digit_vec(&self) -> Vec { + let digits: Vec = self + .to_num() + .to_string() + .chars() + .map(|c| c.to_digit(10).unwrap()) + .collect(); + return digits; + } +} \ No newline at end of file diff --git a/tests/test.rs b/tests/test.rs index 1ee2040..27c547c 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,4 +1,4 @@ -use decimal128::*; +use decimal128; #[test] fn it_returns_negative_infinity() { From b9bc1d428284dbc5dbbb44290ad5e4373673b709 Mon Sep 17 00:00:00 2001 From: lrlna Date: Wed, 22 Jul 2020 22:59:48 +0200 Subject: [PATCH 2/5] use u16/u128 for exponent/significand in dec128 --- Cargo.lock | 179 ------------ Cargo.toml | 5 - src/exponent.rs | 32 +- src/lib.rs | 716 +++++++++++++++++++++++---------------------- src/significand.rs | 42 +-- tests/test.rs | 2 +- 6 files changed, 389 insertions(+), 587 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ea4debb..937a170 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,185 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "autocfg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "backtrace" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bitvec" -version = "0.17.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "radium 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "byteorder" -version = "1.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cc" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "decimal128" version = "0.1.0" -dependencies = [ - "bitvec 0.17.4 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "either" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "failure" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure_derive" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.32 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libc" -version = "0.2.51" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro2" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quote" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "radium" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rustc-demangle" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "syn" -version = "0.15.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "synstructure" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.32 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -[metadata] -"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" -"checksum backtrace 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f106c02a3604afcdc0df5d36cc47b44b55917dbaf3d808f71c163a0ddba64637" -"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" -"checksum bitvec 0.17.4 (registry+https://github.com/rust-lang/crates.io-index)" = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" -"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" -"checksum cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5f3fee5eeb60324c2781f1e41286bdee933850fff9b3c672587fed5ec58c83" -"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" -"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" -"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" -"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" -"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" -"checksum proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)" = "ba92c84f814b3f9a44c5cfca7d2ad77fa10710867d2bbb1b3d175ab5f47daa12" -"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" -"checksum radium 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" -"checksum rustc-demangle 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ccc78bfd5acd7bf3e89cffcf899e5cb1a52d6fafa8dec2739ad70c9577a57288" -"checksum syn 0.15.32 (registry+https://github.com/rust-lang/crates.io-index)" = "846620ec526c1599c070eff393bfeeeb88a93afa2513fc3b49f1fea84cf7b0ed" -"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index a7c47bd..a961c6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,3 @@ edition = "2018" license = "Apache-2.0" name = "decimal128" version = "0.1.0" - -[dependencies] -bitvec = "0.17.4" -byteorder = "1.3.4" -failure = "0.1.2" diff --git a/src/exponent.rs b/src/exponent.rs index 985ebe4..8b7d06e 100644 --- a/src/exponent.rs +++ b/src/exponent.rs @@ -1,38 +1,20 @@ -use bitvec::prelude::*; -use byteorder::{ByteOrder, BigEndian, ReadBytesExt}; -use std::io::Cursor; - #[derive(Clone, PartialEq, PartialOrd)] // where msb0 is big endian. -pub struct Exponent { - vec: BitVec, +pub(crate) struct Exponent { + inner: u16, } /// Exponent is a 14-bit portion of decimal128 that follows the sign bit. Here we /// are storing it as a 16-bit BitVec that can be later converted to a u16. impl Exponent { - pub fn new() -> Self { - Exponent { - vec: bitvec![Msb0, u8; 0; 2], - } - } - - pub fn append(&mut self, vec: &mut BitVec) { - self.vec.append(vec) - } - - pub fn is_zero(&self) -> bool { - self.to_num() == 0 - } - - pub fn to_num(&self) -> u16 { - let mut reader = Cursor::new(&self.vec); - reader.read_u16::().unwrap() + pub(crate) fn new() -> Self { + Exponent { inner: 0 } } // compare current exponent value with exponent bias (largest possible // exponent value) // TODO: check if 6176 (exponent bias) can be stored as u16 pub fn to_adjusted(&self) -> i16 { - self.to_num() as i16 - 6176 as i16 + // NOTE: this could potentially panic if self.inner is larger than max i16 + self.inner as i16 - 6176 as i16 } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index b8324a9..32f1e9a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,76 +2,76 @@ //! [1bits] [ 14bits ] [ 113 bits ] //! sign exponent significand //! field -use bitvec::prelude::*; -use std::cmp::Ordering; -use std::fmt; -use std::str::FromStr; -mod significand; +// use std::cmp::Ordering; +// use std::fmt; +// use std::str::FromStr; + mod exponent; +mod significand; -pub use significand::Significand; -pub use exponent::Exponent; +use exponent::Exponent; +use significand::Significand; #[derive(Clone)] pub struct Decimal128 { - pub sign: bool, - pub exponent: Exponent, - pub significand: Significand, - pub bytes: [u8; 16], + sign: bool, + exp: u16, + sig: u128, + bytes: [u8; 16], nan: bool, inf: bool, } -pub enum NumberType { +enum NumberType { NaN, Infinity, Finite, } - -impl From for Decimal128 { - fn from(_v: i32) -> Self { - unimplemented!("Creating Decimal128 from i32 is not yet implemented.") - } -} - -impl From for Decimal128 { - fn from(_v: u32) -> Self { - unimplemented!("Creating Decimal128 from u32 is not yet implemented.") - } -} - -impl FromStr for Decimal128 { - type Err = (); - fn from_str(_s: &str) -> Result { - unimplemented!("Creating Decimal128 from string is not yet implemented.") - } -} - -impl Into for Decimal128 { - fn into(self) -> i32 { - unimplemented!("Creating i32 from Decimal128 is not yet implemented.") - } -} - -impl Into for Decimal128 { - fn into(self) -> u32 { - unimplemented!("Creating u32 from Decimal128 is not yet implemented.") - } -} - +// +// impl From for Decimal128 { +// fn from(_v: i32) -> Self { +// unimplemented!("Creating Decimal128 from i32 is not yet implemented.") +// } +// } +// +// impl From for Decimal128 { +// fn from(_v: u32) -> Self { +// unimplemented!("Creating Decimal128 from u32 is not yet implemented.") +// } +// } +// +// impl FromStr for Decimal128 { +// type Err = (); +// fn from_str(_s: &str) -> Result { +// unimplemented!("Creating Decimal128 from string is not yet implemented.") +// } +// } +// +// impl Into for Decimal128 { +// fn into(self) -> i32 { +// unimplemented!("Creating i32 from Decimal128 is not yet implemented.") +// } +// } +// +// impl Into for Decimal128 { +// fn into(self) -> u32 { +// unimplemented!("Creating u32 from Decimal128 is not yet implemented.") +// } +// } +// impl Decimal128 { - pub fn zero() -> Self { - Decimal128 { - sign: false, - exponent: Exponent::new(), - significand: Significand::new(), - bytes: [0u8; 16], - nan: false, - inf: false, - } - } - + // pub fn zero() -> Self { + // Decimal128 { + // sign: false, + // exponent: Exponent::new(), + // significand: Significand::new(), + // bytes: [0u8; 16], + // nan: false, + // inf: false, + // } + // } + // /// Create a Decimal128 from a [u8; 16]. /// /// This method extracts out the sign, exponent and signficand, uses Binary @@ -86,14 +86,22 @@ impl Decimal128 { /// let dec128 = Decimal128::from_raw_bytes(vec); /// ``` pub fn from_raw_bytes(buffer: [u8; 16]) -> Self { - // decimal 128's exponent is 14bits long; we will construct a u16 and - // fill up the first two bits as zeros and then get its value. - let mut total_exp = Exponent::new(); - // Significnad can be 113 *or* 111 bit long. Regardless of the size we - // will pad it with 14 0s. We will be eventually constructing a u128 - // from this eventually. - let mut total_sig = Significand::new(); + let mut num = Decimal128 { + sign: false, + // decimal 128's exponent is 14bits long. We will construct a u16 to + // begin with. The first two bits will be 0, and the rest will be + // swapped out as bits come in. + exp: 0, + // Significand can be 113 *or* 111 bit long. It will start off as a + // u128. The first 14 bits will be 0s and the rest will be swapped out + // as the rest of the bits come in. + sig: 0, + bytes: [0u8; 16], + nan: false, + inf: false, + }; + // first byte let byte = buffer[0]; let max = 0b1111_1111; // first bit is sign: negative or positive integer @@ -112,6 +120,7 @@ impl Decimal128 { let combination_field = match res { // if everything is 1s, we are looking at NaN 0b1111_1111 => NumberType::NaN, + // TODO: clarify comment // if the last of the five bits is a 0, we are looking at Infinity 0b1111_1011 => NumberType::Infinity, // match for finite cases @@ -119,24 +128,37 @@ impl Decimal128 { 0b1111_1111 => { // since the first two bits after the sign are 11, we ignore // them and gather the remainder of the first byte. - let c = if (byte | 0b1110_1111) == max { 1 } else { 0 }; - let d = if (byte | 0b1111_0111) == max { 1 } else { 0 }; - let e = if (byte | 0b1111_1011) == max { 1 } else { 0 }; - let f = if (byte | 0b1111_1101) == max { 1 } else { 0 }; - let g = if (byte | 0b1111_1110) == max { 1 } else { 0 }; - let mut exp = bitvec![c, d, e, f, g]; - total_exp.append(&mut exp); - // in this case second byte of the buffer can just be - // straight up appended to the exponent. - let byte_2 = buffer[1]; - let mut second_byte_bitvec = BitVec::from_element(byte_2); - total_exp.append(&mut second_byte_bitvec); + // + // 16 bits total: + // - 2 zeroes + // - 5 exponent bits + // - 8 more exponent bits + // - 1 more exponent bit + if (byte | 0b1110_1111) == max { + num.exp |= 1 << 13; + } + if (byte | 0b1111_0111) == max { + num.exp |= 1 << 12; + } + if (byte | 0b1111_1011) == max { + num.exp |= 1 << 11; + } + if (byte | 0b1111_1101) == max { + num.exp |= 1 << 10; + } + if (byte | 0b1111_1110) == max { + num.exp |= 1 << 9; + } + + // fill the u16 exponent with the entire second byte from bit 7 to bit 15. + num.exp |= (buffer[1] as u16) << 1; + // out of the third byte the first bit is part of the // exponent, and the last 7 bits are part of the significand - let byte_3 = buffer[1]; - let h = if (byte_2 | 0b0111_1111) == max { 1 } else { 0 }; - let mut exp_cont = bitvec![h]; - total_exp.append(&mut exp_cont); + let byte_3 = buffer[2]; + if (byte_3 | 0b0111_1111) == max { + num.exp |= 1; + } let i = if (byte_3 | 0b1011_1111) == max { 1 } else { 0 }; let j = if (byte_3 | 0b1101_1111) == max { 1 } else { 0 }; let k = if (byte_3 | 0b1110_1111) == max { 1 } else { 0 }; @@ -225,274 +247,274 @@ impl Decimal128 { }; dec128 } - - pub fn is_nan(&self) -> bool { - if self.nan { - return true; - } else { - return false; - } - } - - pub fn is_negative(&self) -> bool { - if self.sign { - return true; - } else { - return false; - } - } - - pub fn is_positive(&self) -> bool { - return !self.is_negative(); - } - - pub fn is_zero(&self) -> bool { - return !self.nan && self.exponent.is_zero() && self.significand.is_zero() - } - - /// Converts Decimal128 to string. Uses information in - /// [speleotrove](http://speleotrove.com/decimal/daconvs.html) decimal - /// documentation. - pub fn to_string(&self) -> String { - // just return NaN if we are dealing with NaN. This does not come with a - // sign. - if self.nan { - return String::from("NaN"); - }; - - // Everything else can have a sign. We can create a string from Infinity - // or a Finite number. - let str = if self.inf { - "Infinity".to_string() - } else { - self.create_string() - }; - - // add a sign if this is a negative number - return if !self.sign { str } else { format!("-{}", str) }; - } - - /// Returns raw bytes. - pub fn to_raw_bytes(&self) -> [u8; 16] { - self.bytes - } - - fn create_string(&self) -> String { - if self.use_scientific_notation() { - let exp_sign = if self.exponent.to_adjusted() < 0 { - "" - } else { - "+" - }; - - if self.significand.as_digit_vec().len() > 1 { - let mut first_significand = self.significand.as_digit_vec().clone(); - // we already used the first digit, so only stringify the - // remainder of the significand - let remainder_significand = stringify_vec(first_significand.split_off(1)); - return format!( - "{first_significand}.{remainder_significand}E{exp_sign}{scientific_exponent}", - first_significand = first_significand[0], - remainder_significand = remainder_significand, - exp_sign = exp_sign, - scientific_exponent = self.scientific_exponent() - ); - } else { - return format!( - "{significand}E{exp_sign}{scientific_exponent}", - significand = self.significand.to_num(), - exp_sign = exp_sign, - scientific_exponent = self.scientific_exponent() - ); - } - } else if self.exponent.to_adjusted() < 0 { - if self.significand.count_digits() > self.exponent.to_adjusted().abs() { - let dec_point = self.get_decimal_point_index() as usize; - let mut significand_vec = self.significand.as_digit_vec().clone(); - let remainder_significand = stringify_vec(significand_vec.split_off(dec_point - 1)); - return format!( - "{first_significand}.{remainder_significand}", - first_significand = significand_vec[0], - remainder_significand = remainder_significand - ); - } else { - let zero_pad = self.get_zero_padding(); - return format!( - "0.{zero_pad}{significand}", - zero_pad = zero_pad, - significand = self.significand.to_num() - ); - } - } - format!("{}", self.significand.to_num()) - } - - fn use_scientific_notation(&self) -> bool { - (self.exponent.to_adjusted() as i16) > 0 || (self.scientific_exponent() as i16) < -6 - } - - fn scientific_exponent(&self) -> i16 { - // first variable is number of digits in a significand - (self.significand.count_digits() - 1) + self.exponent.to_adjusted() - } - - // for larger numbers we want to know where to put the decimal point. - fn get_decimal_point_index(&self) -> i16 { - self.significand.count_digits() - self.exponent.to_adjusted().abs() - } - - // for very small decimals, we need to know how many zeroes to pad it with. - fn get_zero_padding(&self) -> String { - let left_zero_pad_count = - (self.exponent.to_adjusted() + self.significand.count_digits()).abs(); - std::iter::repeat("0") - .take(left_zero_pad_count as usize) - .collect::() - } - - /// create a compare functiont that returns a decimal 128 that's either: - /// * -1 = less than - /// * 0 = equal - /// * 1 = greater than - /// When comparing and orderign Decimal128, we should end up with: - /// (-) NaN | -Infinity | x < 0 | -0 | +0 | x > 0 | +Infinity | (+) NaN - /// - /// Even though NaN can't be negative or positive, when reading the sign bit, - /// (-) NaN < (+) NaN // - // TODO: once we have a method to create Decimal128 from another number type - // (u32/i32/u128/i128), change this return type to be a Decimal128 as well. - pub fn compare(&self, other: &Decimal128) -> isize { - let self_exp = self.exponent.to_adjusted(); - let other_exp = other.exponent.to_adjusted(); - let self_signif = self.significand.to_num(); - let other_signif = other.significand.to_num(); - - // NaN and Infinity will be ordered via the sign Check - if self.sign > other.sign { - -1 - } else if self.sign < other.sign { - 1 - } else { - // since 1x10^3 is the same number as 10x10^2, we want to try to - // even out the exponents before comparing significands. - let exp_dif = (self_exp - other_exp).abs(); - // however, if the difference is greeater than 66, they are - // definitely diffferent numbers. so we only try to mingle with - // exponents if the difference is less than 66. - if exp_dif <= 66 { - if self_exp < other_exp { - Decimal128::increase_exponent(self_signif, self_exp, other_exp); - Decimal128::decrease_exponent(other_signif, other_exp, self_exp); - } else if self_exp > other_exp { - Decimal128::decrease_exponent(self_signif, self_exp, other_exp); - Decimal128::increase_exponent(other_signif, other_exp, self_exp); - } - } - if self_exp == other_exp { - if self_signif > other_signif { - 1 - } else if self_signif < other_signif { - -1 - } else { - 0 - } - } else { - if self_exp > other_exp { - 1 - } else if self_exp < other_exp { - -1 - } else { - 0 - } - } - } - } - - // This is part of the effort to compare two different Decimal128 numbers. - fn increase_exponent(mut significand: u128, mut exponent: i16, goal: i16) { - if significand == 0 as u128 { - exponent = goal - } - - while exponent < goal { - let significand_divided_by_10 = significand / 10; - if significand % 10 != 0 { - break; - } - exponent += 1; - significand = significand_divided_by_10 - } - } - - // This is part of the effort to compare two different Decimal128 numbers. - fn decrease_exponent(mut significand: u128, mut exponent: i16, goal: i16) { - if significand == 0 as u128 { - exponent = goal - } - - while exponent > goal { - let significand_times_10 = significand * 10; - if significand_times_10 - Significand::max_value() > 0 { - break; - } - exponent -= 1; - significand = significand_times_10 - } - } -} - -impl fmt::Display for Decimal128 { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{}", self.to_string()) - } -} - -// this should be the same as Display trait -impl fmt::Debug for Decimal128 { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -impl PartialOrd for Decimal128 { - fn partial_cmp(&self, other: &Decimal128) -> Option { - match self.compare(other) { - v if v == 0 => Some(Ordering::Equal), - v if v > 0 => Some(Ordering::Greater), - v if v < 0 => Some(Ordering::Less), - _ => None, - } - } -} - -impl PartialEq for Decimal128 { - fn eq(&self, other: &Decimal128) -> bool { - self.compare(other) == 0 - } -} - -/// Format Decimal128 as an engineering string -/// TODO: this currently only uses the default to_string method for Decimal128 -/// and needs to actually do the engineering string formatting. -impl fmt::LowerExp for Decimal128 { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} -/// Formats Decimal128 to hexadecimal binary representation. -impl fmt::LowerHex for Decimal128 { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - for b in self.bytes.iter().rev() { - write!(fmt, "{:02x}", b)?; - } - Ok(()) - } + // pub fn is_nan(&self) -> bool { + // if self.nan { + // return true; + // } else { + // return false; + // } + // } + // + // pub fn is_negative(&self) -> bool { + // if self.sign { + // return true; + // } else { + // return false; + // } + // } + // + // pub fn is_positive(&self) -> bool { + // return !self.is_negative(); + // } + // + // pub fn is_zero(&self) -> bool { + // return !self.nan && self.exponent.is_zero() && self.significand.is_zero() + // } + // + // /// Converts Decimal128 to string. Uses information in + // /// [speleotrove](http://speleotrove.com/decimal/daconvs.html) decimal + // /// documentation. + // pub fn to_string(&self) -> String { + // // just return NaN if we are dealing with NaN. This does not come with a + // // sign. + // if self.nan { + // return String::from("NaN"); + // }; + // + // // Everything else can have a sign. We can create a string from Infinity + // // or a Finite number. + // let str = if self.inf { + // "Infinity".to_string() + // } else { + // self.create_string() + // }; + // + // // add a sign if this is a negative number + // return if !self.sign { str } else { format!("-{}", str) }; + // } + // + // /// Returns raw bytes. + // pub fn to_raw_bytes(&self) -> [u8; 16] { + // self.bytes + // } + // + // fn create_string(&self) -> String { + // if self.use_scientific_notation() { + // let exp_sign = if self.exponent.to_adjusted() < 0 { + // "" + // } else { + // "+" + // }; + // + // if self.significand.as_digit_vec().len() > 1 { + // let mut first_significand = self.significand.as_digit_vec().clone(); + // // we already used the first digit, so only stringify the + // // remainder of the significand + // let remainder_significand = stringify_vec(first_significand.split_off(1)); + // return format!( + // "{first_significand}.{remainder_significand}E{exp_sign}{scientific_exponent}", + // first_significand = first_significand[0], + // remainder_significand = remainder_significand, + // exp_sign = exp_sign, + // scientific_exponent = self.scientific_exponent() + // ); + // } else { + // return format!( + // "{significand}E{exp_sign}{scientific_exponent}", + // significand = self.significand.to_num(), + // exp_sign = exp_sign, + // scientific_exponent = self.scientific_exponent() + // ); + // } + // } else if self.exponent.to_adjusted() < 0 { + // if self.significand.count_digits() > self.exponent.to_adjusted().abs() { + // let dec_point = self.get_decimal_point_index() as usize; + // let mut significand_vec = self.significand.as_digit_vec().clone(); + // let remainder_significand = stringify_vec(significand_vec.split_off(dec_point - 1)); + // return format!( + // "{first_significand}.{remainder_significand}", + // first_significand = significand_vec[0], + // remainder_significand = remainder_significand + // ); + // } else { + // let zero_pad = self.get_zero_padding(); + // return format!( + // "0.{zero_pad}{significand}", + // zero_pad = zero_pad, + // significand = self.significand.to_num() + // ); + // } + // } + // format!("{}", self.significand.to_num()) + // } + // + // fn use_scientific_notation(&self) -> bool { + // (self.exponent.to_adjusted() as i16) > 0 || (self.scientific_exponent() as i16) < -6 + // } + // + // fn scientific_exponent(&self) -> i16 { + // // first variable is number of digits in a significand + // (self.significand.count_digits() - 1) + self.exponent.to_adjusted() + // } + // + // // for larger numbers we want to know where to put the decimal point. + // fn get_decimal_point_index(&self) -> i16 { + // self.significand.count_digits() - self.exponent.to_adjusted().abs() + // } + // + // // for very small decimals, we need to know how many zeroes to pad it with. + // fn get_zero_padding(&self) -> String { + // let left_zero_pad_count = + // (self.exponent.to_adjusted() + self.significand.count_digits()).abs(); + // std::iter::repeat("0") + // .take(left_zero_pad_count as usize) + // .collect::() + // } + // + // /// create a compare functiont that returns a decimal 128 that's either: + // /// * -1 = less than + // /// * 0 = equal + // /// * 1 = greater than + // /// When comparing and orderign Decimal128, we should end up with: + // /// (-) NaN | -Infinity | x < 0 | -0 | +0 | x > 0 | +Infinity | (+) NaN + // /// + // /// Even though NaN can't be negative or positive, when reading the sign bit, + // /// (-) NaN < (+) NaN + // // + // // TODO: once we have a method to create Decimal128 from another number type + // // (u32/i32/u128/i128), change this return type to be a Decimal128 as well. + // pub fn compare(&self, other: &Decimal128) -> isize { + // let self_exp = self.exponent.to_adjusted(); + // let other_exp = other.exponent.to_adjusted(); + // let self_signif = self.significand.to_num(); + // let other_signif = other.significand.to_num(); + // + // // NaN and Infinity will be ordered via the sign Check + // if self.sign > other.sign { + // -1 + // } else if self.sign < other.sign { + // 1 + // } else { + // // since 1x10^3 is the same number as 10x10^2, we want to try to + // // even out the exponents before comparing significands. + // let exp_dif = (self_exp - other_exp).abs(); + // // however, if the difference is greeater than 66, they are + // // definitely diffferent numbers. so we only try to mingle with + // // exponents if the difference is less than 66. + // if exp_dif <= 66 { + // if self_exp < other_exp { + // Decimal128::increase_exponent(self_signif, self_exp, other_exp); + // Decimal128::decrease_exponent(other_signif, other_exp, self_exp); + // } else if self_exp > other_exp { + // Decimal128::decrease_exponent(self_signif, self_exp, other_exp); + // Decimal128::increase_exponent(other_signif, other_exp, self_exp); + // } + // } + // if self_exp == other_exp { + // if self_signif > other_signif { + // 1 + // } else if self_signif < other_signif { + // -1 + // } else { + // 0 + // } + // } else { + // if self_exp > other_exp { + // 1 + // } else if self_exp < other_exp { + // -1 + // } else { + // 0 + // } + // } + // } + // } + // + // // This is part of the effort to compare two different Decimal128 numbers. + // fn increase_exponent(mut significand: u128, mut exponent: i16, goal: i16) { + // if significand == 0 as u128 { + // exponent = goal + // } + // + // while exponent < goal { + // let significand_divided_by_10 = significand / 10; + // if significand % 10 != 0 { + // break; + // } + // exponent += 1; + // significand = significand_divided_by_10 + // } + // } + // + // // This is part of the effort to compare two different Decimal128 numbers. + // fn decrease_exponent(mut significand: u128, mut exponent: i16, goal: i16) { + // if significand == 0 as u128 { + // exponent = goal + // } + // + // while exponent > goal { + // let significand_times_10 = significand * 10; + // if significand_times_10 - Significand::max_value() > 0 { + // break; + // } + // exponent -= 1; + // significand = significand_times_10 + // } + // } } - -fn stringify_vec(vec: Vec) -> String { - vec.into_iter() - .map(|d| d.to_string()) - .collect::>() - .join("") -} \ No newline at end of file +// +// impl fmt::Display for Decimal128 { +// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { +// write!(fmt, "{}", self.to_string()) +// } +// } +// +// // this should be the same as Display trait +// impl fmt::Debug for Decimal128 { +// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { +// fmt::Display::fmt(self, fmt) +// } +// } +// +// impl PartialOrd for Decimal128 { +// fn partial_cmp(&self, other: &Decimal128) -> Option { +// match self.compare(other) { +// v if v == 0 => Some(Ordering::Equal), +// v if v > 0 => Some(Ordering::Greater), +// v if v < 0 => Some(Ordering::Less), +// _ => None, +// } +// } +// } +// +// impl PartialEq for Decimal128 { +// fn eq(&self, other: &Decimal128) -> bool { +// self.compare(other) == 0 +// } +// } +// +// /// Format Decimal128 as an engineering string +// /// TODO: this currently only uses the default to_string method for Decimal128 +// /// and needs to actually do the engineering string formatting. +// impl fmt::LowerExp for Decimal128 { +// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { +// fmt::Display::fmt(self, fmt) +// } +// } +// /// Formats Decimal128 to hexadecimal binary representation. +// impl fmt::LowerHex for Decimal128 { +// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { +// for b in self.bytes.iter().rev() { +// write!(fmt, "{:02x}", b)?; +// } +// Ok(()) +// } +// } +// +// fn stringify_vec(vec: Vec) -> String { +// vec.into_iter() +// .map(|d| d.to_string()) +// .collect::>() +// .join("") +// } diff --git a/src/significand.rs b/src/significand.rs index 27340cd..7d43c43 100644 --- a/src/significand.rs +++ b/src/significand.rs @@ -1,38 +1,19 @@ -use bitvec::prelude::*; -use byteorder::{BigEndian}; -use std::io::Cursor; - #[derive(Clone, PartialEq, PartialOrd)] // where msb0 is big endian. -pub struct Significand { - vec: BitVec, +pub(crate) struct Significand { + inner: u128, } /// Significand is a padded 111- or 113-bit coefficient. We are storing it as a /// 128-bit BitVec with the padded difference. This can be converted to a u128. impl Significand { - pub fn new() -> Self { - Significand { - vec: bitvec![Msb0, u8; 0; 14], - } - } - - pub fn append(&mut self, vec: &mut BitVec) { - self.vec.append(vec) - } - - pub fn is_zero(&self) -> bool { - // FIXME: Very inefficient, but docs are down - self.count_digits() == 0 - } - - pub fn to_num(&self) -> u128 { - let mut reader = Cursor::new(&self.vec); - // BigEndian::read_u128(&self.vec) - reader.read_u128::().unwrap() + pub(crate) fn new() -> Self { + Significand { inner: 0 } } - pub fn max_value() -> u128 { + // max number from Decimal128 spec + // TODO: document usage better + pub(crate) fn max_value() -> u128 { u128::from_str_radix("9999999999999999999999999999999999", 10).unwrap() } @@ -42,17 +23,18 @@ impl Significand { // // We return a u16 number of digits, as it's easier to compare to the // exponent since that's also stored as a u16. - pub fn count_digits(&self) -> i16 { + // TODO: use a logarithm method for this to remove intermediate allocation in as_digi_vec + pub(crate) fn count_digits(&self) -> i16 { self.as_digit_vec().len() as i16 } - pub fn as_digit_vec(&self) -> Vec { + pub(crate) fn as_digit_vec(&self) -> Vec { let digits: Vec = self - .to_num() + .inner .to_string() .chars() .map(|c| c.to_digit(10).unwrap()) .collect(); return digits; } -} \ No newline at end of file +} diff --git a/tests/test.rs b/tests/test.rs index 27c547c..9708b58 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,4 +1,4 @@ -use decimal128; +use decimal128::Decimal128; #[test] fn it_returns_negative_infinity() { From ce2b5798b8ed57aed97dc30d4aa4918d5fa715ec Mon Sep 17 00:00:00 2001 From: Irina Shestak Date: Wed, 28 Oct 2020 12:48:19 +0100 Subject: [PATCH 3/5] updates from desktop --- Cargo.lock | 1 - src/lib.rs | 137 +++++++++++++++++++++++++++++++++++------------------ 2 files changed, 92 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 937a170..b4c0a03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,4 +3,3 @@ [[package]] name = "decimal128" version = "0.1.0" - diff --git a/src/lib.rs b/src/lib.rs index 32f1e9a..62ae3ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -159,87 +159,135 @@ impl Decimal128 { if (byte_3 | 0b0111_1111) == max { num.exp |= 1; } - let i = if (byte_3 | 0b1011_1111) == max { 1 } else { 0 }; - let j = if (byte_3 | 0b1101_1111) == max { 1 } else { 0 }; - let k = if (byte_3 | 0b1110_1111) == max { 1 } else { 0 }; - let l = if (byte_3 | 0b1111_0111) == max { 1 } else { 0 }; - let m = if (byte_3 | 0b1111_1011) == max { 1 } else { 0 }; - let n = if (byte_3 | 0b1111_1101) == max { 1 } else { 0 }; - let o = if (byte_3 | 0b1111_1110) == max { 1 } else { 0 }; + // significand is 128 bits + // when are shifting our math is based on total of 127. + // the first 14 bits of 128 bits need to be 0s + // 127-14 = 113 + // Start a new vec for 111bit significand. This version of // the significand is offset by two bits, so we pad it with // `100` - let mut sig = bitvec![1, 0, 0, i, j, k, l, m, n, o]; - total_sig.append(&mut sig); + num.sig |= 1 << 113; + num.sig |= 0 << 112; + num.sig |= 0 << 111; + + if (byte | 0b1011_1111) == max { + num.sig |= 1 << 110; + } + if (byte | 0b1101_1111) == max { + num.sig |= 1 << 109; + } + if (byte | 0b1110_1111) == max { + num.sig |= 1 << 108; + } + if (byte | 0b1111_0111) == max { + num.sig |= 1 << 107; + } + if (byte | 0b1111_1011) == max { + num.sig |= 1 << 106; + } + if (byte | 0b1111_1101) == max { + num.sig |= 1 << 105; + } + if (byte | 0b1111_1110) == max { + num.sig |= 1 << 104; + } NumberType::Finite } _ => { // if the first two bits after the sign are `00`, `01`, // `10`, we add the remainder of the first byte to exponent - let a = if (byte | 0b1011_1111) == max { 1 } else { 0 }; - let b = if (byte | 0b1101_1111) == max { 1 } else { 0 }; - let c = if (byte | 0b1110_1111) == max { 1 } else { 0 }; - let d = if (byte | 0b1111_0111) == max { 1 } else { 0 }; - let e = if (byte | 0b1111_1011) == max { 1 } else { 0 }; - let f = if (byte | 0b1111_1101) == max { 1 } else { 0 }; - let g = if (byte | 0b1111_1110) == max { 1 } else { 0 }; - let mut exp = bitvec![a, b, c, d, e, f, g]; - total_exp.append(&mut exp); + + // we are filling up a u16 (16bits), but exponent is only 14, + // so we need to leave the first two bits as 00 + // 15 - 2 = 13 + if (byte | 0b1011_1111) == max { + num.exp |= 1 << 13; + } + if (byte | 0b1101_1111) == max { + num.exp |= 1 << 12; + } + if (byte | 0b1110_1111) == max { + num.exp |= 1 << 11; + } + if (byte | 0b1111_0111) == max { + num.exp |= 1 << 10; + } + if (byte | 0b1111_1011) == max { + num.exp |= 1 << 9; + } + if (byte | 0b1111_1101) == max { + num.exp |= 1 << 8; + } + if (byte | 0b1111_1110) == max { + num.exp |= 1 << 7; + } // out of the second byte the first 7 bits are part of the - // exponent, and the last bit if part of the significand + // exponent, and the last bit is part of the significand let byte_2 = buffer[1]; - let h = if (byte_2 | 0b0111_1111) == max { 1 } else { 0 }; - let i = if (byte_2 | 0b1011_1111) == max { 1 } else { 0 }; - let j = if (byte_2 | 0b1101_1111) == max { 1 } else { 0 }; - let k = if (byte_2 | 0b1110_1111) == max { 1 } else { 0 }; - let l = if (byte_2 | 0b1111_0111) == max { 1 } else { 0 }; - let m = if (byte_2 | 0b1111_1011) == max { 1 } else { 0 }; - let n = if (byte_2 | 0b1111_1101) == max { 1 } else { 0 }; - let mut exp_cont = bitvec![h, i, j, k, l, m, n]; - total_exp.append(&mut exp_cont); - let o = if (byte_2 | 0b1111_1110) == max { 1 } else { 0 }; + if (byte_2 | 0b0111_1111) == max { + num.exp |= 1 << 6; + } + if (byte_2 | 0b1011_1111) == max { + num.exp |= 1 << 5; + } + if (byte_2 | 0b1101_1111) == max { + num.exp |= 1 << 4; + } + if (byte_2 | 0b1110_1111) == max { + num.exp |= 1 << 3; + } + if (byte_2 | 0b1111_0111) == max { + num.exp |= 1 << 2; + } + if (byte_2 | 0b1111_1011) == max { + num.exp |= 1 << 1; + } + if (byte_2 | 0b1111_1101) == max { + num.exp |= 1; + } // Start a new vec for 113bit significand. Since this // version of significand is not offset, we pad it with only // `0` - let mut sig = bitvec![0, o]; - total_sig.append(&mut sig); + num.sig |= 0 << 113; + if (byte_2 | 0b1111_1110) == max { + num.sig |= 112; + } // add the whole third byte to the signficand in this case - let byte_3 = buffer[2]; - let mut third_byte_bitvec = BitVec::from_element(byte_3); - total_sig.append(&mut third_byte_bitvec); + num.sig |= (buffer[2] as u128) << 111; NumberType::Finite } }, }; - // the rest of the bytes of the vec we are passed in. + // the rest of the bytes of the vec we are paC + // significand. We can bit shift them all in. + // 111 - 8 = 103 for bytes in 3..buffer.len() { - let mut bitvec = BitVec::from_element(buffer[bytes]); - // let mut bv: BitVec = (&[buffer[bytes]] as &[u8]).into(); - total_sig.append(&mut bitvec); + num.sig |= (buffer[bytes] as u128) << 103; } let dec128 = match combination_field { NumberType::Finite => Decimal128 { sign, - exponent: total_exp, - significand: total_sig, + exp: num.exp, + sig: num.sig, bytes: buffer, nan: false, inf: false, }, NumberType::NaN => Decimal128 { sign, - exponent: total_exp, - significand: total_sig, + exp: num.exp, + sig: num.sig, bytes: buffer, nan: true, inf: false, }, NumberType::Infinity => Decimal128 { sign, - exponent: total_exp, - significand: total_sig, + exp: num.exp, + sig: num.sig, bytes: buffer, nan: false, inf: true, @@ -247,7 +295,6 @@ impl Decimal128 { }; dec128 } - // // pub fn is_nan(&self) -> bool { // if self.nan { // return true; From cfe0c5fc68244ec761f687ff8a8f11896d6a0811 Mon Sep 17 00:00:00 2001 From: Irina Shestak Date: Wed, 28 Oct 2020 16:51:04 +0100 Subject: [PATCH 4/5] adapt all other methods to the new significand and exponent calculations --- src/exponent.rs | 20 -- src/lib.rs | 635 +++++++++++++++++++++++---------------------- src/significand.rs | 40 --- 3 files changed, 328 insertions(+), 367 deletions(-) delete mode 100644 src/exponent.rs delete mode 100644 src/significand.rs diff --git a/src/exponent.rs b/src/exponent.rs deleted file mode 100644 index 8b7d06e..0000000 --- a/src/exponent.rs +++ /dev/null @@ -1,20 +0,0 @@ -#[derive(Clone, PartialEq, PartialOrd)] -// where msb0 is big endian. -pub(crate) struct Exponent { - inner: u16, -} -/// Exponent is a 14-bit portion of decimal128 that follows the sign bit. Here we -/// are storing it as a 16-bit BitVec that can be later converted to a u16. -impl Exponent { - pub(crate) fn new() -> Self { - Exponent { inner: 0 } - } - - // compare current exponent value with exponent bias (largest possible - // exponent value) - // TODO: check if 6176 (exponent bias) can be stored as u16 - pub fn to_adjusted(&self) -> i16 { - // NOTE: this could potentially panic if self.inner is larger than max i16 - self.inner as i16 - 6176 as i16 - } -} diff --git a/src/lib.rs b/src/lib.rs index 62ae3ae..e11ac48 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,15 +3,9 @@ //! sign exponent significand //! field -// use std::cmp::Ordering; -// use std::fmt; -// use std::str::FromStr; - -mod exponent; -mod significand; - -use exponent::Exponent; -use significand::Significand; +use std::cmp::Ordering; +use std::fmt; +use std::str::FromStr; #[derive(Clone)] pub struct Decimal128 { @@ -28,38 +22,38 @@ enum NumberType { Infinity, Finite, } -// -// impl From for Decimal128 { -// fn from(_v: i32) -> Self { -// unimplemented!("Creating Decimal128 from i32 is not yet implemented.") -// } -// } -// -// impl From for Decimal128 { -// fn from(_v: u32) -> Self { -// unimplemented!("Creating Decimal128 from u32 is not yet implemented.") -// } -// } -// -// impl FromStr for Decimal128 { -// type Err = (); -// fn from_str(_s: &str) -> Result { -// unimplemented!("Creating Decimal128 from string is not yet implemented.") -// } -// } -// -// impl Into for Decimal128 { -// fn into(self) -> i32 { -// unimplemented!("Creating i32 from Decimal128 is not yet implemented.") -// } -// } -// -// impl Into for Decimal128 { -// fn into(self) -> u32 { -// unimplemented!("Creating u32 from Decimal128 is not yet implemented.") -// } -// } -// + +impl From for Decimal128 { + fn from(_v: i32) -> Self { + unimplemented!("Creating Decimal128 from i32 is not yet implemented.") + } +} + +impl From for Decimal128 { + fn from(_v: u32) -> Self { + unimplemented!("Creating Decimal128 from u32 is not yet implemented.") + } +} + +impl FromStr for Decimal128 { + type Err = (); + fn from_str(_s: &str) -> Result { + unimplemented!("Creating Decimal128 from string is not yet implemented.") + } +} + +impl Into for Decimal128 { + fn into(self) -> i32 { + unimplemented!("Creating i32 from Decimal128 is not yet implemented.") + } +} + +impl Into for Decimal128 { + fn into(self) -> u32 { + unimplemented!("Creating u32 from Decimal128 is not yet implemented.") + } +} + impl Decimal128 { // pub fn zero() -> Self { // Decimal128 { @@ -159,6 +153,7 @@ impl Decimal128 { if (byte_3 | 0b0111_1111) == max { num.exp |= 1; } + // significand is 128 bits // when are shifting our math is based on total of 127. // the first 14 bits of 128 bits need to be 0s @@ -260,7 +255,7 @@ impl Decimal128 { }, }; - // the rest of the bytes of the vec we are paC + // the rest of the bytes of the vec are part of the // significand. We can bit shift them all in. // 111 - 8 = 103 for bytes in 3..buffer.len() { @@ -295,273 +290,299 @@ impl Decimal128 { }; dec128 } - // pub fn is_nan(&self) -> bool { - // if self.nan { - // return true; - // } else { - // return false; - // } - // } - // - // pub fn is_negative(&self) -> bool { - // if self.sign { - // return true; - // } else { - // return false; - // } - // } - // - // pub fn is_positive(&self) -> bool { - // return !self.is_negative(); - // } - // - // pub fn is_zero(&self) -> bool { - // return !self.nan && self.exponent.is_zero() && self.significand.is_zero() - // } - // - // /// Converts Decimal128 to string. Uses information in - // /// [speleotrove](http://speleotrove.com/decimal/daconvs.html) decimal - // /// documentation. - // pub fn to_string(&self) -> String { - // // just return NaN if we are dealing with NaN. This does not come with a - // // sign. - // if self.nan { - // return String::from("NaN"); - // }; - // - // // Everything else can have a sign. We can create a string from Infinity - // // or a Finite number. - // let str = if self.inf { - // "Infinity".to_string() - // } else { - // self.create_string() - // }; - // - // // add a sign if this is a negative number - // return if !self.sign { str } else { format!("-{}", str) }; - // } - // - // /// Returns raw bytes. - // pub fn to_raw_bytes(&self) -> [u8; 16] { - // self.bytes - // } - // - // fn create_string(&self) -> String { - // if self.use_scientific_notation() { - // let exp_sign = if self.exponent.to_adjusted() < 0 { - // "" - // } else { - // "+" - // }; - // - // if self.significand.as_digit_vec().len() > 1 { - // let mut first_significand = self.significand.as_digit_vec().clone(); - // // we already used the first digit, so only stringify the - // // remainder of the significand - // let remainder_significand = stringify_vec(first_significand.split_off(1)); - // return format!( - // "{first_significand}.{remainder_significand}E{exp_sign}{scientific_exponent}", - // first_significand = first_significand[0], - // remainder_significand = remainder_significand, - // exp_sign = exp_sign, - // scientific_exponent = self.scientific_exponent() - // ); - // } else { - // return format!( - // "{significand}E{exp_sign}{scientific_exponent}", - // significand = self.significand.to_num(), - // exp_sign = exp_sign, - // scientific_exponent = self.scientific_exponent() - // ); - // } - // } else if self.exponent.to_adjusted() < 0 { - // if self.significand.count_digits() > self.exponent.to_adjusted().abs() { - // let dec_point = self.get_decimal_point_index() as usize; - // let mut significand_vec = self.significand.as_digit_vec().clone(); - // let remainder_significand = stringify_vec(significand_vec.split_off(dec_point - 1)); - // return format!( - // "{first_significand}.{remainder_significand}", - // first_significand = significand_vec[0], - // remainder_significand = remainder_significand - // ); - // } else { - // let zero_pad = self.get_zero_padding(); - // return format!( - // "0.{zero_pad}{significand}", - // zero_pad = zero_pad, - // significand = self.significand.to_num() - // ); - // } - // } - // format!("{}", self.significand.to_num()) - // } - // - // fn use_scientific_notation(&self) -> bool { - // (self.exponent.to_adjusted() as i16) > 0 || (self.scientific_exponent() as i16) < -6 - // } - // - // fn scientific_exponent(&self) -> i16 { - // // first variable is number of digits in a significand - // (self.significand.count_digits() - 1) + self.exponent.to_adjusted() - // } - // - // // for larger numbers we want to know where to put the decimal point. - // fn get_decimal_point_index(&self) -> i16 { - // self.significand.count_digits() - self.exponent.to_adjusted().abs() - // } - // - // // for very small decimals, we need to know how many zeroes to pad it with. - // fn get_zero_padding(&self) -> String { - // let left_zero_pad_count = - // (self.exponent.to_adjusted() + self.significand.count_digits()).abs(); - // std::iter::repeat("0") - // .take(left_zero_pad_count as usize) - // .collect::() - // } - // - // /// create a compare functiont that returns a decimal 128 that's either: - // /// * -1 = less than - // /// * 0 = equal - // /// * 1 = greater than - // /// When comparing and orderign Decimal128, we should end up with: - // /// (-) NaN | -Infinity | x < 0 | -0 | +0 | x > 0 | +Infinity | (+) NaN - // /// - // /// Even though NaN can't be negative or positive, when reading the sign bit, - // /// (-) NaN < (+) NaN - // // - // // TODO: once we have a method to create Decimal128 from another number type - // // (u32/i32/u128/i128), change this return type to be a Decimal128 as well. - // pub fn compare(&self, other: &Decimal128) -> isize { - // let self_exp = self.exponent.to_adjusted(); - // let other_exp = other.exponent.to_adjusted(); - // let self_signif = self.significand.to_num(); - // let other_signif = other.significand.to_num(); - // - // // NaN and Infinity will be ordered via the sign Check - // if self.sign > other.sign { - // -1 - // } else if self.sign < other.sign { - // 1 - // } else { - // // since 1x10^3 is the same number as 10x10^2, we want to try to - // // even out the exponents before comparing significands. - // let exp_dif = (self_exp - other_exp).abs(); - // // however, if the difference is greeater than 66, they are - // // definitely diffferent numbers. so we only try to mingle with - // // exponents if the difference is less than 66. - // if exp_dif <= 66 { - // if self_exp < other_exp { - // Decimal128::increase_exponent(self_signif, self_exp, other_exp); - // Decimal128::decrease_exponent(other_signif, other_exp, self_exp); - // } else if self_exp > other_exp { - // Decimal128::decrease_exponent(self_signif, self_exp, other_exp); - // Decimal128::increase_exponent(other_signif, other_exp, self_exp); - // } - // } - // if self_exp == other_exp { - // if self_signif > other_signif { - // 1 - // } else if self_signif < other_signif { - // -1 - // } else { - // 0 - // } - // } else { - // if self_exp > other_exp { - // 1 - // } else if self_exp < other_exp { - // -1 - // } else { - // 0 - // } - // } - // } - // } + + pub fn is_nan(&self) -> bool { + self.nan + } + + pub fn is_negative(&self) -> bool { + self.sign + } + + pub fn is_positive(&self) -> bool { + !self.is_negative() + } + + pub fn is_zero(&self) -> bool { + !self.nan && self.exp == 0 && self.sig == 0 + } + + /// Converts Decimal128 to string. Uses information in + /// [speleotrove](http://speleotrove.com/decimal/daconvs.html) decimal + /// documentation. + pub fn to_string(&self) -> String { + // Just return the string 'NaN' if we are dealing with NaN. + // This does *not* come with a 'sign'. + if self.nan { + return String::from("NaN"); + }; + + // Everything else can have a sign. + + // We can create a string from 'Infinity' or a Finite number. + let str = if self.inf { + "Infinity".to_string() + } else { + self.create_string() + }; + + // Specifically add a '-' sign to our string if this is a negative number. + return if !self.sign { str } else { format!("-{}", str) }; + } + + /// Returns raw bytes. + pub fn to_raw_bytes(&self) -> [u8; 16] { + self.bytes + } + + fn create_string(&self) -> String { + if self.use_scientific_notation() { + let exp_sign = if self.adjust_exponent() < 0 { + "" + } else { + "+" + }; + + if self.sig_as_digit_vec().len() > 1 { + let mut first_significand = self.sig_as_digit_vec().clone(); + // We already used the first digit, so only stringify the + // remainder of the significand. + let remainder_significand = stringify_vec(first_significand.split_off(1)); + return format!( + "{first_significand}.{remainder_significand}E{exp_sign}{scientific_exponent}", + first_significand = first_significand[0], + remainder_significand = remainder_significand, + exp_sign = exp_sign, + scientific_exponent = self.scientific_exponent() + ); + } else { + return format!( + "{significand}E{exp_sign}{scientific_exponent}", + significand = self.sig, + exp_sign = exp_sign, + scientific_exponent = self.scientific_exponent() + ); + } + } else if self.adjust_exponent() < 0 { + if self.count_sig_digits() > self.adjust_exponent().abs() { + let dec_point = self.get_decimal_point_index() as usize; + let mut significand_vec = self.sig_as_digit_vec().clone(); + let remainder_significand = stringify_vec(significand_vec.split_off(dec_point - 1)); + return format!( + "{first_significand}.{remainder_significand}", + first_significand = significand_vec[0], + remainder_significand = remainder_significand + ); + } else { + let zero_pad = self.get_zero_padding(); + return format!( + "0.{zero_pad}{significand}", + zero_pad = zero_pad, + significand = self.sig + ); + } + } + format!("{}", self.sig) + } + + fn use_scientific_notation(&self) -> bool { + self.adjust_exponent() > 0 || (self.scientific_exponent() as i16) < -6 + } + + fn scientific_exponent(&self) -> i16 { + // first variable is number of digits in a significand + (self.count_sig_digits() - 1) + self.adjust_exponent() + } + + // Compare current exponent value with exponent bias, 6176, which is also the largest + // possible exponent value. // - // // This is part of the effort to compare two different Decimal128 numbers. - // fn increase_exponent(mut significand: u128, mut exponent: i16, goal: i16) { - // if significand == 0 as u128 { - // exponent = goal - // } + // We convert exp and exponent bias to an i16 as this number _can_ be signed. + fn adjust_exponent(&self) -> i16 { + self.exp as i16 - 6176 as i16 + } + + // Convert significand into a vec of numbers. + // For example, if the number is 128765, the vec will look like [1, 2, 8, 7, 6, 5] + fn sig_as_digit_vec(&self) -> Vec { + let digits: Vec = self.sig + .to_string() + .chars() + .map(|c| c.to_digit(10).unwrap()) + .collect(); + return digits; + } + + // Count the number of digits in the significand. // - // while exponent < goal { - // let significand_divided_by_10 = significand / 10; - // if significand % 10 != 0 { - // break; - // } - // exponent += 1; - // significand = significand_divided_by_10 - // } - // } + // This method first converts significand into a digit vec. // - // // This is part of the effort to compare two different Decimal128 numbers. - // fn decrease_exponent(mut significand: u128, mut exponent: i16, goal: i16) { - // if significand == 0 as u128 { - // exponent = goal - // } + // We then return a u16 number of digits, as it's easier to compare to the + // exponent since that's also stored as a u16. + fn count_sig_digits(&self) -> i16 { + self.sig_as_digit_vec().len() as i16 + } + + // Determines where to put the decimal point for larger numbers. + fn get_decimal_point_index(&self) -> i16 { + self.count_sig_digits() - self.adjust_exponent().abs() + } + + // Determines how many zeroes to pad smaller numbers with. + fn get_zero_padding(&self) -> String { + let left_zero_pad_count: i16 = + (self.adjust_exponent() + self.count_sig_digits()).abs(); + std::iter::repeat("0") + .take(left_zero_pad_count as usize) + .collect::() + } + + /// Creates a compare function that returns a decimal 128 that's either: + /// * -1 = less than + /// * 0 = equal + /// * 1 = greater than + /// + /// When comparing and ordering Decimal128, we should end up with: + /// (-) NaN | -Infinity | x < 0 | -0 | +0 | x > 0 | +Infinity | (+) NaN + /// + /// Even though NaN can't be negative or positive, when reading the sign bit, + /// (-) NaN < (+) NaN // - // while exponent > goal { - // let significand_times_10 = significand * 10; - // if significand_times_10 - Significand::max_value() > 0 { - // break; - // } - // exponent -= 1; - // significand = significand_times_10 - // } - // } + // TODO: once we have a method to create Decimal128 from another number type + // (u32/i32/u128/i128), change this return type to be a Decimal128 as well. + pub fn compare(&self, other: &Decimal128) -> isize { + let self_exp = self.adjust_exponent(); + let other_exp = other.adjust_exponent(); + let self_sig = self.sig; + let other_sig = other.sig; + + // NaN and Infinity will be ordered via the sign Check + if self.sign > other.sign { + -1 + } else if self.sign < other.sign { + 1 + } else { + // since 1x10^3 is the same number as 10x10^2, we want to try to + // even out the exponents before comparing significands. + let exp_dif = (self_exp - other_exp).abs(); + // however, if the difference is greater than 66, they are + // definitely diffferent numbers. so we only try to mingle with + // exponents if the difference is less than 66. + if exp_dif <= 66 { + if self_exp < other_exp { + Decimal128::increase_exponent(self_sig, self_exp, other_exp); + Decimal128::decrease_exponent(other_sig, other_exp, self_exp); + } else if self_exp > other_exp { + Decimal128::decrease_exponent(self_sig, self_exp, other_exp); + Decimal128::increase_exponent(other_sig, other_exp, self_exp); + } + } + if self_exp == other_exp { + if self_sig > other_sig { + 1 + } else if self_sig < other_sig { + -1 + } else { + 0 + } + } else { + if self_exp > other_exp { + 1 + } else if self_exp < other_exp { + -1 + } else { + 0 + } + } + } + } + + // This is part of the effort to compare two different Decimal128 numbers. + fn increase_exponent(mut significand: u128, mut exponent: i16, goal: i16) { + if significand == 0 as u128 { + exponent = goal + } + + while exponent < goal { + let significand_divided_by_10 = significand / 10; + if significand % 10 != 0 { + break; + } + exponent += 1; + significand = significand_divided_by_10 + } + } + + // This is part of the effort to compare two different Decimal128 numbers. + fn decrease_exponent(mut significand: u128, mut exponent: i16, goal: i16) { + let max_sig = u128::from_str_radix("9999999999999999999999999999999999", 10).unwrap(); + if significand == 0 as u128 { + exponent = goal + } + + while exponent > goal { + let significand_times_10 = significand * 10; + if (significand_times_10 - max_sig) > 0 { + break; + } + exponent -= 1; + significand = significand_times_10 + } + } +} + +impl fmt::Display for Decimal128 { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}", self.to_string()) + } +} + + // this should be the same as Display trait + impl fmt::Debug for Decimal128 { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } + } + + impl PartialOrd for Decimal128 { + fn partial_cmp(&self, other: &Decimal128) -> Option { + match self.compare(other) { + v if v == 0 => Some(Ordering::Equal), + v if v > 0 => Some(Ordering::Greater), + v if v < 0 => Some(Ordering::Less), + _ => None, + } + } + } + + impl PartialEq for Decimal128 { + fn eq(&self, other: &Decimal128) -> bool { + self.compare(other) == 0 + } + } + + /// Format Decimal128 as an engineering string + /// TODO: this currently only uses the default to_string method for Decimal128 + /// and needs to actually do the engineering string formatting. + impl fmt::LowerExp for Decimal128 { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } + } + /// Formats Decimal128 to hexadecimal binary representation. + impl fmt::LowerHex for Decimal128 { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + for b in self.bytes.iter().rev() { + write!(fmt, "{:02x}", b)?; + } + Ok(()) + } + } + + +fn stringify_vec(vec: Vec) -> String { + vec.into_iter() + .map(|d| d.to_string()) + .collect::>() + .join("") } -// -// impl fmt::Display for Decimal128 { -// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { -// write!(fmt, "{}", self.to_string()) -// } -// } -// -// // this should be the same as Display trait -// impl fmt::Debug for Decimal128 { -// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { -// fmt::Display::fmt(self, fmt) -// } -// } -// -// impl PartialOrd for Decimal128 { -// fn partial_cmp(&self, other: &Decimal128) -> Option { -// match self.compare(other) { -// v if v == 0 => Some(Ordering::Equal), -// v if v > 0 => Some(Ordering::Greater), -// v if v < 0 => Some(Ordering::Less), -// _ => None, -// } -// } -// } -// -// impl PartialEq for Decimal128 { -// fn eq(&self, other: &Decimal128) -> bool { -// self.compare(other) == 0 -// } -// } -// -// /// Format Decimal128 as an engineering string -// /// TODO: this currently only uses the default to_string method for Decimal128 -// /// and needs to actually do the engineering string formatting. -// impl fmt::LowerExp for Decimal128 { -// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { -// fmt::Display::fmt(self, fmt) -// } -// } -// /// Formats Decimal128 to hexadecimal binary representation. -// impl fmt::LowerHex for Decimal128 { -// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { -// for b in self.bytes.iter().rev() { -// write!(fmt, "{:02x}", b)?; -// } -// Ok(()) -// } -// } -// -// fn stringify_vec(vec: Vec) -> String { -// vec.into_iter() -// .map(|d| d.to_string()) -// .collect::>() -// .join("") -// } diff --git a/src/significand.rs b/src/significand.rs deleted file mode 100644 index 7d43c43..0000000 --- a/src/significand.rs +++ /dev/null @@ -1,40 +0,0 @@ -#[derive(Clone, PartialEq, PartialOrd)] -// where msb0 is big endian. -pub(crate) struct Significand { - inner: u128, -} - -/// Significand is a padded 111- or 113-bit coefficient. We are storing it as a -/// 128-bit BitVec with the padded difference. This can be converted to a u128. -impl Significand { - pub(crate) fn new() -> Self { - Significand { inner: 0 } - } - - // max number from Decimal128 spec - // TODO: document usage better - pub(crate) fn max_value() -> u128 { - u128::from_str_radix("9999999999999999999999999999999999", 10).unwrap() - } - - // count the number of digits in the significand. This method first converts - // significand BitVec into a u128 number, then converts it to string to - // count characters and collects them in a vec to look at the vec's length. - // - // We return a u16 number of digits, as it's easier to compare to the - // exponent since that's also stored as a u16. - // TODO: use a logarithm method for this to remove intermediate allocation in as_digi_vec - pub(crate) fn count_digits(&self) -> i16 { - self.as_digit_vec().len() as i16 - } - - pub(crate) fn as_digit_vec(&self) -> Vec { - let digits: Vec = self - .inner - .to_string() - .chars() - .map(|c| c.to_digit(10).unwrap()) - .collect(); - return digits; - } -} From 4d25b4c7d95ebc7053cddb3dd2ddea7f81d077c5 Mon Sep 17 00:00:00 2001 From: Irina Shestak Date: Wed, 28 Oct 2020 17:28:27 +0100 Subject: [PATCH 5/5] better commenting on significand calculations --- src/lib.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e11ac48..a0273dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -154,9 +154,16 @@ impl Decimal128 { num.exp |= 1; } - // significand is 128 bits - // when are shifting our math is based on total of 127. - // the first 14 bits of 128 bits need to be 0s + // Significand u128: + // - 14 zeroes + // - 1 0 0 padding + // - 7 significand bits + // - 13 bytes + // + // Significand is 128 bits. + // When are shifting our math is based on total of 127. + // The first 14 bits of 128 bits need to be 0s. + // Staring bitshifting number math: // 127-14 = 113 // Start a new vec for 111bit significand. This version of @@ -304,7 +311,7 @@ impl Decimal128 { } pub fn is_zero(&self) -> bool { - !self.nan && self.exp == 0 && self.sig == 0 + !self.nan && self.exp == 0 && self.count_sig_digits() == 0 } /// Converts Decimal128 to string. Uses information in