Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 53 additions & 15 deletions core/src/num/bigrat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -673,21 +673,27 @@ impl BigRat {
let num_digits_of_int_part = formatted_integer_part.value.num_digits();
// reduce decimal places by however many digits we already printed
// in the integer portion
//
// saturate to zero in case we already exhausted all digits and
// shouldn't print any decimal places
let dp = sf.saturating_sub(num_digits_of_int_part);
if integer_part == 0.into() {
// if the integer part is 0, we don't want leading zeroes
// after the decimal point to affect the number of non-zero
// digits printed

// we add 1 to the number of decimal places in this case because
// the integer component of '0' shouldn't count against the
// number of significant figures
MaxDigitsToPrint::DpButIgnoreLeadingZeroes(dp + 1)

// If we truncated the integer part (e.g. 12 to 1 sf -> 10),
// do NOT try to round up based on the decimal fraction.
if num_digits_of_int_part > sf {
MaxDigitsToPrint::DecimalPlacesNoRounding(0)
} else {
MaxDigitsToPrint::DecimalPlaces(dp)
// saturate to zero in case we already exhausted all digits and
// shouldn't print any decimal places
let dp = sf.saturating_sub(num_digits_of_int_part);
if integer_part == 0.into() {
// if the integer part is 0, we don't want leading zeroes
// after the decimal point to affect the number of non-zero
// digits printed

// we add 1 to the number of decimal places in this case because
// the integer component of '0' shouldn't count against the
// number of significant figures
MaxDigitsToPrint::DpButIgnoreLeadingZeroes(dp + 1)
} else {
MaxDigitsToPrint::DecimalPlaces(dp)
}
}
} else {
MaxDigitsToPrint::DecimalPlaces(10)
Expand Down Expand Up @@ -751,6 +757,12 @@ impl BigRat {
// reached the end of the number
return Err(NextDigitErr::Terminated { round_up: false });
}
// Explicitly handle the NoRounding case
if let MaxDigitsToPrint::DecimalPlacesNoRounding(limit) = max_digits {
if i == limit {
return Err(NextDigitErr::Terminated { round_up: false });
}
}
if max_digits == MaxDigitsToPrint::DecimalPlaces(i)
|| max_digits == MaxDigitsToPrint::DpButIgnoreLeadingZeroes(i)
{
Expand Down Expand Up @@ -886,7 +898,31 @@ impl BigRat {
sign
};
if round_up {
// todo
let mut chars: Vec<char> = trailing_digits.chars().collect();
let mut carry = true;
for i in (0..chars.len()).rev() {
let c = chars[i];
if c == decimal_separator.decimal_separator() {
continue;
}
if let Some(d) = c.to_digit(base.base_as_u8().into()) {
if d + 1 < base.base_as_u8().into() {
chars[i] =
char::from_digit(d + 1, base.base_as_u8().into()).unwrap();
carry = false;
break;
}
chars[i] = '0';
} else {
chars.insert(i + 1, '1');
carry = false;
break;
}
}
if carry {
chars.insert(0, '1');
}
trailing_digits = chars.into_iter().collect();
}
// is the number exact, or did we need to truncate?
let exact = current_numerator == 0.into();
Expand Down Expand Up @@ -1134,6 +1170,8 @@ enum MaxDigitsToPrint {
DecimalPlaces(usize),
/// Print only the given number of dps, but ignore leading zeroes after the decimal point
DpButIgnoreLeadingZeroes(usize),
/// Print digits but strictly truncate at the limit (no rounding up)
DecimalPlacesNoRounding(usize),
}

impl ops::Neg for BigRat {
Expand Down
10 changes: 10 additions & 0 deletions core/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4296,6 +4296,16 @@ fn sf_small_2() {
test_eval("pi / 1000000 to 2 sf", "approx. 0.0000031");
}

#[test]
fn sf_rounding_integer_carry() {
test_eval("123.9 to 3 sf", "approx. 124");
}

#[test]
fn sf_rounding_small_decimal() {
test_eval("0.00555 to 2 sf", "approx. 0.0056");
}

#[test]
fn sf_small_3() {
test_eval("pi / 1000000 to 3 sf", "approx. 0.00000314");
Expand Down
Loading