Implement RocDec to [u8] algorithm.

This commit is contained in:
Derek Gustafson 2022-03-04 22:52:13 -05:00
parent dc92de7781
commit a021c09752
No known key found for this signature in database
GPG key ID: 8B8B15EF3CC8410B
2 changed files with 51 additions and 26 deletions

View file

@ -326,9 +326,7 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
Ok(result) Ok(result)
} }
Layout::Builtin(Builtin::Decimal) => { Layout::Builtin(Builtin::Decimal) => Ok(helper!(RocDec)),
Ok(helper!(RocDec))
}
Layout::Builtin(Builtin::Str) => { Layout::Builtin(Builtin::Str) => {
let size = layout.stack_size(env.target_info) as usize; let size = layout.stack_size(env.target_info) as usize;
Ok( Ok(

View file

@ -1,12 +1,11 @@
#![crate_type = "lib"] #![crate_type = "lib"]
#![no_std] // #![no_std]
use core::ffi::c_void; use core::ffi::c_void;
use core::fmt; use core::fmt;
use core::mem::{ManuallyDrop, MaybeUninit}; use core::mem::{ManuallyDrop, MaybeUninit};
use core::ops::Drop; use core::ops::Drop;
use core::str; use core::str;
// use roc_error_macros::internal_error; use std::io::Write;
// uncomment when we figure out why it fails below.
mod rc; mod rc;
mod roc_list; mod roc_list;
@ -217,9 +216,10 @@ impl RocDec {
pub const MIN: Self = Self(i128::MIN); pub const MIN: Self = Self(i128::MIN);
pub const MAX: Self = Self(i128::MAX); pub const MAX: Self = Self(i128::MAX);
pub const DECIMAL_PLACES: u32 = 18; const DECIMAL_PLACES: usize = 18;
const ONE_POINT_ZERO: i128 = 10i128.pow(Self::DECIMAL_PLACES as u32);
pub const ONE_POINT_ZERO: i128 = 10i128.pow(Self::DECIMAL_PLACES); const MAX_DIGITS: usize = 39;
const MAX_STR_LENGTH: usize = Self::MAX_DIGITS + 2; // + 2 here to account for the sign & decimal dot
#[allow(clippy::should_implement_trait)] #[allow(clippy::should_implement_trait)]
pub fn from_str(value: &str) -> Option<Self> { pub fn from_str(value: &str) -> Option<Self> {
@ -234,7 +234,7 @@ impl RocDec {
}; };
let opt_after_point = match parts.next() { let opt_after_point = match parts.next() {
Some(answer) if answer.len() <= Self::DECIMAL_PLACES as usize => Some(answer), Some(answer) if answer.len() <= Self::DECIMAL_PLACES => Some(answer),
_ => None, _ => None,
}; };
@ -250,7 +250,7 @@ impl RocDec {
Ok(answer) => { Ok(answer) => {
// Translate e.g. the 1 from 0.1 into 10000000000000000000 // Translate e.g. the 1 from 0.1 into 10000000000000000000
// by "restoring" the elided trailing zeroes to the number! // by "restoring" the elided trailing zeroes to the number!
let trailing_zeroes = Self::DECIMAL_PLACES as usize - after_point.len(); let trailing_zeroes = Self::DECIMAL_PLACES - after_point.len();
let lo = answer * 10i128.pow(trailing_zeroes as u32); let lo = answer * 10i128.pow(trailing_zeroes as u32);
if !before_point.starts_with('-') { if !before_point.starts_with('-') {
@ -269,7 +269,7 @@ impl RocDec {
// Calculate the high digits - the ones before the decimal point. // Calculate the high digits - the ones before the decimal point.
match before_point.parse::<i128>() { match before_point.parse::<i128>() {
Ok(answer) => match answer.checked_mul(10i128.pow(Self::DECIMAL_PLACES)) { Ok(answer) => match answer.checked_mul(Self::ONE_POINT_ZERO) {
Some(hi) => hi.checked_add(lo).map(Self), Some(hi) => hi.checked_add(lo).map(Self),
None => None, None => None,
}, },
@ -281,29 +281,56 @@ impl RocDec {
Self::from_str(val).unwrap().0 Self::from_str(val).unwrap().0
} }
fn to_str_helper(&self, bytes: &mut [u8; 1]) { fn to_str_helper(&self, bytes: &mut [u8; Self::MAX_STR_LENGTH]) {
bytes[0] = 0; if self.0 == 0 {
// TODO write!(&mut bytes[..], "{}", "0.0").unwrap();
return;
}
let is_negative = (self.0 < 0) as usize;
write!(&mut bytes[..], "{:019}", self.0).unwrap();
// By using the :019 format, we're guaranteeing that numbers less than 1, say 0.01234
// get their leading zeros placed in bytes for us. i.e. bytes = b"0012340000000000000"
// If self represents 1234.5678, then bytes is b"1234567800000000000000".
let mut i = Self::DECIMAL_PLACES;
// Find the last place where we have actual data.
while bytes[i] == 0 {
i = i - 1;
}
// At this point i is 21 because bytes[21] is the final '0' in b"1234567800000000000000".
let decimal_location = i - Self::DECIMAL_PLACES + 1 + is_negative;
// decimal_location = 4
while bytes[i] == ('0' as u8) {
bytes[i] = 0;
i = i - 1;
}
// Now i = 7, because bytes[7] = '8', and bytes = b"12345678"
while i >= decimal_location {
bytes[i + 1] = bytes[i];
}
// Now i = 4, and bytes = b"123455678"
bytes[i] = '.' as u8;
// Finally bytes = b"1234.5678"
} }
pub fn to_str(&self) -> RocStr { pub fn to_str(&self) -> RocStr {
let mut bytes: [u8; 1] = [0]; let mut bytes = [0 as u8; Self::MAX_STR_LENGTH];
self.to_str_helper(&mut bytes); self.to_str_helper(&mut bytes);
unsafe {RocStr::from_slice(&bytes) } unsafe { RocStr::from_slice(&bytes) }
} }
} }
impl fmt::Display for RocDec { impl fmt::Display for RocDec {
fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut bytes: [u8; 1] = [0]; let mut bytes = [0 as u8; Self::MAX_STR_LENGTH];
self.to_str_helper(&mut bytes); self.to_str_helper(&mut bytes);
match str::from_utf8(&bytes) { let result = unsafe { str::from_utf8_unchecked(&bytes) };
Ok(slice) => write!(fmtr, "{}", slice), write!(fmtr, "{}", result)
Err(payload) => panic!("Error in converting RocDec({}) to a string: {}", self.0, payload),
// Err(payload) => internal_error!("Error in converting RocDec({}) to a string: {}", self.0, payload),
// This raises a compile error: can't find eprintln
// Is this because we don't use std?
}
} }
} }