Skip to content

Commit c6a7534

Browse files
printf: make negative values wrap around with unsigned/hex format
To convert from negative to unsigned with overflow, we get the two's complement representation of the absolute (unsigned) value.
1 parent e6ff6d5 commit c6a7534

File tree

2 files changed

+70
-12
lines changed

2 files changed

+70
-12
lines changed

src/uucore/src/lib/features/parser/num_parser.rs

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use bigdecimal::{
1111
BigDecimal,
1212
num_bigint::{BigInt, BigUint, Sign},
1313
};
14+
use num_traits::FromPrimitive;
1415
use num_traits::Signed;
1516
use num_traits::ToPrimitive;
1617
use num_traits::Zero;
@@ -166,22 +167,26 @@ impl ExtendedParser for u64 {
166167
let (digits, scale) = bd.into_bigint_and_scale();
167168
if scale == 0 {
168169
let negative = digits.sign() == Sign::Minus;
169-
match u64::try_from(digits) {
170+
171+
match u64::try_from(digits.clone()) {
170172
Ok(i) => Ok(i),
171-
_ => Err(ExtendedParserError::Overflow(if negative {
172-
// TODO: We should wrap around here #7488
173-
0
174-
} else {
175-
u64::MAX
176-
})),
173+
_ => {
174+
if negative {
175+
match u64::try_from(digits.into_parts().1) {
176+
Ok(i) => Ok(!i + 1), // two's complement representation
177+
_ => Err(ExtendedParserError::Overflow(u64::MAX)),
178+
}
179+
} else {
180+
Err(ExtendedParserError::Overflow(u64::MAX))
181+
}
182+
}
177183
}
178184
} else {
179185
// Should not happen.
180186
Err(ExtendedParserError::NotNumeric)
181187
}
182188
}
183-
// TODO: Handle -0 too #7488
184-
// No other case should not happen.
189+
ExtendedBigDecimal::MinusZero => Ok(0),
185190
_ => Err(ExtendedParserError::NotNumeric),
186191
}
187192
}
@@ -500,10 +505,28 @@ mod tests {
500505
fn test_decimal_u64() {
501506
assert_eq!(Ok(123), u64::extended_parse("123"));
502507
assert_eq!(Ok(u64::MAX), u64::extended_parse(&format!("{}", u64::MAX)));
503-
// TODO: We should wrap around here #7488
508+
assert_eq!(Ok(0), u64::extended_parse("-0"));
509+
assert_eq!(Ok(u64::MAX), u64::extended_parse("-1"));
510+
assert_eq!(
511+
Ok(u64::MAX / 2 + 1),
512+
u64::extended_parse("-9223372036854775808") // i64::MIN
513+
);
514+
assert_eq!(
515+
Ok(1123372036854675616),
516+
u64::extended_parse("-17323372036854876000") // 2*i64::MIN
517+
);
518+
assert_eq!(Ok(1), u64::extended_parse("-18446744073709551615")); // -u64::MAX
519+
assert!(matches!(
520+
u64::extended_parse("-18446744073709551616"), // -u64::MAX - 1
521+
Err(ExtendedParserError::Overflow(u64::MAX))
522+
));
523+
assert!(matches!(
524+
u64::extended_parse("-92233720368547758150"),
525+
Err(ExtendedParserError::Overflow(u64::MAX))
526+
));
504527
assert!(matches!(
505-
u64::extended_parse("-123"),
506-
Err(ExtendedParserError::Overflow(0))
528+
u64::extended_parse("-170141183460469231731687303715884105729"),
529+
Err(ExtendedParserError::Overflow(u64::MAX))
507530
));
508531
assert!(matches!(
509532
u64::extended_parse(""),

tests/by-util/test_printf.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,41 @@ fn partial_integer() {
668668
.stderr_is("printf: '42x23': value not completely converted\n");
669669
}
670670

671+
#[test]
672+
fn unsigned_hex_negative_wraparound() {
673+
new_ucmd!()
674+
.args(&["%x", "-0b100"])
675+
.succeeds()
676+
.stdout_only("fffffffffffffffc");
677+
678+
new_ucmd!()
679+
.args(&["%x", "-0100"])
680+
.succeeds()
681+
.stdout_only("ffffffffffffffc0");
682+
683+
new_ucmd!()
684+
.args(&["%x", "-100"])
685+
.succeeds()
686+
.stdout_only("ffffffffffffff9c");
687+
688+
new_ucmd!()
689+
.args(&["%x", "-0x100"])
690+
.succeeds()
691+
.stdout_only("ffffffffffffff00");
692+
693+
new_ucmd!()
694+
.args(&["%x", "-92233720368547758150"])
695+
.fails_with_code(1)
696+
.stdout_is("ffffffffffffffff")
697+
.stderr_is("printf: '-92233720368547758150': Numerical result out of range\n");
698+
699+
new_ucmd!()
700+
.args(&["%u", "-1002233720368547758150"])
701+
.fails_with_code(1)
702+
.stdout_is("18446744073709551615")
703+
.stderr_is("printf: '-1002233720368547758150': Numerical result out of range\n");
704+
}
705+
671706
#[test]
672707
fn test_overflow() {
673708
new_ucmd!()

0 commit comments

Comments
 (0)