difftreelog
fix prefer shorter exponent in std.format
in: master
2 files changed
crates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth338 nums338 nums339 } else {339 } else {340 let (mantissa, exp) = decompose_f64(iv);340 let (mantissa, exp) = decompose_f64(iv);341 let mut v = BigUint::from(mantissa) << u32::try_from(exp).expect("huge f64 exp fits u32");341 let mut v = BigUint::from(mantissa) << exp.unsigned_abs();342 let radix = BigUint::from(radix as u64);342 let radix = BigUint::from(radix as u64);343 let mut nums = Vec::new();343 let mut nums = Vec::new();344 while v != BigUint::ZERO {344 while v != BigUint::ZERO {526526527/// bool signifies carry, e.g 0.999 at precision 2 results in 1.000, thus carry.527/// bool signifies carry, e.g 0.999 at precision 2 results in 1.000, thus carry.528// floor((2 * low_bits * 10**prec + 2**k) / 2**(k+1))528// floor((2 * low_bits * 10**prec + 2**k) / 2**(k+1))529fn round_frac(abs: f64, precision: u16) -> (Frac, bool) {529fn round_frac(abs: f64, prec: u16) -> (Frac, bool) {530 let prec = usize::from(precision);531 let (mantissa, exp) = decompose_f64(abs);530 let (mantissa, exp) = decompose_f64(abs);532 if exp >= 0 {531 if exp >= 0 {533 // abs is integral: no fractional bits.532 // abs is integral: no fractional bits.534 return (Frac::Small(0), false);533 return (Frac::Small(0), false);535 }534 }536 let k = u32::try_from(-exp).expect("exp fits u32");535 let k = exp.unsigned_abs();537 // Fractional part is exactly `low_bits / 2**k` (mantissa is 53 bits).536 // Fractional part is exactly `low_bits / 2**k` (mantissa is 53 bits).538 let low_bits = if k >= 64 {537 let low_bits = if k >= 64 {539 mantissa538 mantissa546 round_frac_u128(low_bits, k, prec).unwrap_or_else(|| round_frac_big(low_bits, k, prec))545 round_frac_u128(low_bits, k, prec).unwrap_or_else(|| round_frac_big(low_bits, k, prec))547}546}548547549fn round_frac_u128(low_bits: u64, k: u32, prec: usize) -> Option<(Frac, bool)> {548fn round_frac_u128(low_bits: u64, k: u32, prec: u16) -> Option<(Frac, bool)> {550 if k > 126 {549 if k > 126 {551 return None;550 return None;552 }551 }553 let mut ten_pow = 1u128;552 let ten_pow = 10u128.checked_pow(u32::from(prec))?;554 for _ in 0..prec {555 ten_pow = ten_pow.checked_mul(10)?;556 }557 let numerator = u128::from(low_bits)553 let numerator = u128::from(low_bits)558 .checked_mul(ten_pow)?554 .checked_mul(ten_pow)?559 .checked_mul(2)?555 .checked_mul(2)?566 })562 })567}563}568564569fn round_frac_big(low_bits: u64, k: u32, prec: usize) -> (Frac, bool) {565fn round_frac_big(low_bits: u64, k: u32, prec: u16) -> (Frac, bool) {570 let mut ten_pow = BigUint::from(1u32);571 let ten = BigUint::from(10u32);566 let ten_pow = BigUint::from(10u32).pow(u32::from(prec));572 for _ in 0..prec {573 ten_pow *= &ten;574 }575 let numerator = ((BigUint::from(low_bits) * &ten_pow) << 1u32) + (BigUint::from(1u32) << k);567 let frac = round_ratio(576 let frac = numerator >> (k + 1);568 &(BigUint::from(low_bits) * &ten_pow),569 &(BigUint::from(1u32) << k),570 );577 if frac == ten_pow {571 if frac == ten_pow {578 (Frac::Small(0), true)572 (Frac::Small(0), true)579 } else {573 } else {580 (Frac::Big(frac), false)574 (Frac::Big(frac), false)581 }575 }582}576}577578fn round_ratio(num: &BigUint, den: &BigUint) -> BigUint {579 ((num << 1u32) + den) / (den << 1u32)580}581582fn shorter_exponent(value: f64, sig: u16) -> i32 {583 if value == 0.0 {584 return 0;585 }586 let abs = value.abs();587 let e0 = abs.log10().floor() as i32;588 if rounds_up_to_next_pow10(abs, sig, e0) {589 e0 + 1590 } else {591 e0592 }593}594595fn rounds_up_to_next_pow10(abs: f64, sig: u16, e0: i32) -> bool {596 let (mantissa, exp) = decompose_f64(abs);597 // abs * 10**(sig-1-e0) == num / den598 let t = i32::from(sig) - 1 - e0;599 let mut num = BigUint::from(mantissa);600 let mut den = BigUint::from(1u32);601 let exp_abs = exp.unsigned_abs();602 if exp >= 0 {603 num <<= exp_abs;604 } else {605 den <<= exp_abs;606 }607 let pow10_t_abs = BigUint::from(10u32).pow(t.unsigned_abs());608 if t >= 0 {609 num *= pow10_t_abs;610 } else {611 den *= pow10_t_abs;612 }613 round_ratio(&num, &den) >= BigUint::from(10u32).pow(u32::from(sig))614}583615584#[allow(clippy::fn_params_excessive_bools)]616#[allow(clippy::fn_params_excessive_bools)]585pub fn render_float_sci(617pub fn render_float_sci(594 caps: bool,626 caps: bool,595) {627) {596 let exponent = if n == 0.0 {628 let exponent = if n == 0.0 {597 0.0629 0598 } else {630 } else {599 n.abs().log10().floor()631 shorter_exponent(n, precision.saturating_add(1))600 };632 };601633602 let mantissa = if exponent as i16 == -324 {634 let mantissa = if exponent == -324 {603 n * 10.0 / 10.0_f64.powf(exponent + 1.0)635 n * 10.0 / 10.0_f64.powi(exponent + 1)604 } else {636 } else {605 n / 10.0_f64.powf(exponent)637 n / 10.0_f64.powi(exponent)606 };638 };607 let mut exponent_str = String::new();639 let mut exponent_str = String::new();608 render_decimal(640 render_decimal(609 &mut exponent_str,641 &mut exponent_str,610 exponent < 0.0,642 exponent < 0,611 exponent.abs(),643 f64::from(exponent.abs()),612 3,644 3,613 0,645 0,614 false,646 false,713 }745 }714 ConvTypeV::Shorter => {746 ConvTypeV::Shorter => {715 let value = f64::from_untyped(value.clone())?;747 let value = f64::from_untyped(value.clone())?;716 let exponent = if value == 0.0 {748 let fpprec = fpprec.max(1);717 0.0749 let exponent = shorter_exponent(value, fpprec);718 } else {719 value.abs().log10().floor()720 };721 if exponent < -4.0 || exponent >= f64::from(fpprec) {750 if exponent < -4 || exponent >= i32::from(fpprec) {722 render_float_sci(751 render_float_sci(723 &mut tmp_out,752 &mut tmp_out,724 value,753 value,731 code.caps,760 code.caps,732 );761 );733 } else {762 } else {734 let digits_before_pt = 1.max(exponent as u16 + 1);763 let frac_prec = (i32::from(fpprec) - 1 - exponent).max(0) as u16;735 render_float(764 render_float(736 &mut tmp_out,765 &mut tmp_out,737 value,766 value,738 padding,767 padding,739 fpprec - digits_before_pt,768 frac_prec,740 clfags.blank,769 clfags.blank,741 clfags.sign,770 clfags.sign,742 clfags.alt,771 clfags.alt,tests/suite/rounding.jsonnetdiffbeforeafterbothno changes