-
Notifications
You must be signed in to change notification settings - Fork 19
Critical Generic comparison bug found #123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -443,14 +443,42 @@ pub struct Generic<T> { | |
| scaling_factor: Fraction, | ||
| } | ||
|
|
||
| impl<T: TimeInt> Generic<T> { | ||
| /// Try to create a new, equivalent `Generic` with the given _scaling factor_ | ||
| fn try_into_scaling_factor(self, scaling_factor: Fraction) -> Result<Self, ConversionError> { | ||
| let new_int = TimeInt::checked_mul_fraction( | ||
| &self.integer, | ||
| &self | ||
| .scaling_factor | ||
| .checked_div(&scaling_factor) | ||
| .ok_or(ConversionError::Overflow)?, | ||
| ) | ||
| .ok_or(ConversionError::Overflow)?; | ||
|
|
||
| Ok(Self::new(new_int, scaling_factor)) | ||
| } | ||
| } | ||
|
|
||
| impl<T: TimeInt> PartialOrd<Generic<T>> for Generic<T> { | ||
| /// See [Comparisons](trait.Duration.html#comparisons) | ||
| fn partial_cmp(&self, rhs: &Generic<T>) -> Option<core::cmp::Ordering> { | ||
| Some( | ||
| self.integer | ||
| .checked_mul_fraction(&self.scaling_factor)? | ||
| .cmp(&rhs.integer.checked_mul_fraction(&rhs.scaling_factor)?), | ||
| ) | ||
| if self.scaling_factor == rhs.scaling_factor { | ||
| Some(self.integer.cmp(&rhs.integer)) | ||
| } else if self.scaling_factor < rhs.scaling_factor { | ||
| // convert to the smaller scaling factor (rhs -> self) | ||
| // if conversion fails, we know self is less than rhs | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It feels like this will be true most of the time, but I'm not entirely convinced that there aren't edge cases where
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have stopped contributing here and created |
||
| match rhs.try_into_scaling_factor(self.scaling_factor) { | ||
| Ok(converted_rhs) => Some(self.integer.cmp(&converted_rhs.integer)), | ||
| Err(_) => Some(core::cmp::Ordering::Less), | ||
| } | ||
| } else { | ||
| // convert to the smaller scaling factor (self -> rhs) | ||
| // if conversion fails, we know rhs is less than self | ||
| match self.try_into_scaling_factor(rhs.scaling_factor) { | ||
| Ok(converted_self) => Some(converted_self.integer.cmp(&rhs.integer)), | ||
| Err(_) => Some(core::cmp::Ordering::Greater), | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -884,9 +912,9 @@ pub mod units { | |
| /// See [Comparisons](trait.Duration.html#comparisons) | ||
| fn partial_cmp(&self, rhs: &$big<RhsInt>) -> Option<core::cmp::Ordering> { | ||
| match Self::try_from(*rhs) { | ||
| Ok(rhs) => Some(self.integer().cmp(&rhs.integer())), | ||
| Err(_) => Some(core::cmp::Ordering::Less), | ||
| } | ||
| Ok(rhs) => Some(self.integer().cmp(&rhs.integer())), | ||
| Err(_) => Some(core::cmp::Ordering::Less), | ||
| } | ||
| } | ||
| } | ||
| )+ | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -32,6 +32,14 @@ pub trait TimeInt: | |||||||||||||||||
| fn checked_div_fraction(&self, fraction: &Fraction) -> Option<Self> { | ||||||||||||||||||
| self.checked_mul_fraction(&fraction.recip()) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /// Moves an integer into a comparable base for checking | ||||||||||||||||||
| fn checked_same_base(&self, fraction: &Fraction, rhs_fraction: &Fraction) -> Option<Self> { | ||||||||||||||||||
| let a_n = *fraction.numerator(); | ||||||||||||||||||
| let b_d = *rhs_fraction.denominator(); | ||||||||||||||||||
|
|
||||||||||||||||||
| self.checked_mul(&(b_d.into()))?.checked_mul(&(a_n.into())) | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
+35
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is now unused in the PR after the refactor:
Suggested change
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have stopped contributing here and created |
||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| impl TimeInt for u32 {} | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -56,6 +56,53 @@ fn comparisons() { | |
| Generic::new(1_000u32, Fraction::new(1, 1_000)) | ||
| == Generic::new(2_000u32, Fraction::new(1, 2_000)) | ||
| ); | ||
|
|
||
| // Generic comparisons | ||
| assert!( | ||
| Generic::new(100u32, Fraction::new(1, 1000)) == Generic::new(10u32, Fraction::new(1, 100)) | ||
| ); | ||
| assert!( | ||
| Generic::new(100u32, Fraction::new(1, 1000)) <= Generic::new(10u32, Fraction::new(1, 100)) | ||
| ); | ||
| assert!( | ||
| Generic::new(100u32, Fraction::new(1, 1000)) <= Generic::new(11u32, Fraction::new(1, 100)) | ||
| ); | ||
| assert!( | ||
| Generic::new(200u32, Fraction::new(1, 1000)) >= Generic::new(10u32, Fraction::new(1, 100)) | ||
| ); | ||
| assert!( | ||
| Generic::new(200u32, Fraction::new(1, 1000)) >= Generic::new(10u32, Fraction::new(1, 100)) | ||
| ); | ||
|
Comment on lines
+70
to
+75
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two test cases are identical. These should either be de-duplicated, one of these changed to cover a different part of the comparison space, or a comment added as to why the duplicate is present.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have stopped contributing here and created |
||
| assert!( | ||
| Generic::new(100u32, Fraction::new(1, 1000)) != Generic::new(100u32, Fraction::new(1, 100)) | ||
| ); | ||
| assert!( | ||
| Generic::new(101u32, Fraction::new(1, 1000)) > Generic::new(100u32, Fraction::new(1, 1000)) | ||
| ); | ||
| assert!( | ||
| Generic::new(u32::MAX, Fraction::new(1, 100)) | ||
| > Generic::new(100u32, Fraction::new(1, 1000)) | ||
| ); | ||
|
|
||
| /* Generic comparison stress test: | ||
| loop { | ||
| let mut rands: [u32; 6] = [0; 6]; | ||
| for rand in rands.iter_mut() { | ||
| unsafe { | ||
| loop { | ||
| core::arch::x86_64::_rdrand32_step(rand); | ||
| if *rand != 0 { | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| println!("rands: {:#?}", rands); | ||
| assert!( | ||
| Generic::new(rands[0], Fraction::new(rands[1], rands[2])) | ||
| != Generic::new(rands[3], Fraction::new(rands[4], rands[5])) | ||
| ); | ||
| } */ | ||
|
Comment on lines
+87
to
+105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be useful to rewrite this using the
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have stopped contributing here and created |
||
| } | ||
|
|
||
| #[test] | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to help make what is going on here a little clearer:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have stopped contributing here and created
fugitinstead.