git.delta.rocks / jrsonnet / refs/commits / ecbe9eb36517

difftreelog

fix prefer shorter exponent in std.format

ukpsyrnqYaroslav Bolyukin2026-06-20parent: #2286073.patch.diff
in: master

2 files changed

modifiedcrates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth
338 nums338 nums
339 } 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 {
526526
527/// 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 mantissa
546 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}
548547
549fn 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}
568564
569fn 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}
577
578fn round_ratio(num: &BigUint, den: &BigUint) -> BigUint {
579 ((num << 1u32) + den) / (den << 1u32)
580}
581
582fn 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 + 1
590 } else {
591 e0
592 }
593}
594
595fn 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 / den
598 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}
583615
584#[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 0
598 } else {630 } else {
599 n.abs().log10().floor()631 shorter_exponent(n, precision.saturating_add(1))
600 };632 };
601633
602 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,
addedtests/suite/rounding.jsonnetdiffbeforeafterboth

no changes