-
Notifications
You must be signed in to change notification settings - Fork 51
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
Ratio::<T>::from_...()
- definition of being representable?
#113
Comments
No, this should be Is there a case of "The conversion yields a denominator of 0." that's not already covered by your other points?
|
Yes, I confirmed that the function returns In other words: Would it be correct to change the sentence to (
Couldn't the docs of the
|
It doesn't result in a denominator of 0 though -- the inner conversion returns The only reason that floats can define division by zero as +/-infinity is that there is also a difference between +/-0.0, so you can kind of treat it mathematically as "the limit as the denominator approaches zero from above" or "from below." There's no such distinction with integer ratios, which is why I'm resisting your statement. :)
Sure, it could do that, referring to the individual types or their trait |
How can the cases be described where the conversion yields a denominator of 0? As a user of the conversion function, I'd like to know at least somewhat deterministic details, so that I can be sure that it can't happen sporadically. For
|
By inspection, I'm not sure what cases reach that denominator of 0. I think that may just be defensive coding, but it could be interesting to put a panic in there and fuzz-test until you hit it. :) |
I put a // Overflow
if d1.is_zero() {
println!("{}", val);
return None;
} (By intermediately putting it before the condition, I confirmed that it wasn't optimized away in the following code.) Then I ran the following code (see comments): // Converting every `f32` bit pattern takes me 15-25 minutes in a debug build with a single thread.
for i in 0..=u32::MAX {
let from_val = unsafe { mem::transmute(i) };
// Problems:
// - `4_294_967_300_f32` (5 above `u32::MAX`; float bits equivalent to `0x4f80_0000_u32`)
Ratio::<u32>::from_f32(from_val);
// Problems:
// - `2_147_483_600_f32` (47 below `i32::MAX`; float bits equivalent to `0x4f00_0000_u32`)
// - `-2_147_483_600_f32` (48 above `i32::MIN`; float bits equivalent to `0xcf00_0000_u32`)
// Ratio::<i32>::from_f32(from_val);
// No problems.
// Ratio::<i16>::from_f32(from_val);
// No problems.
// Ratio::<u32>::from_u32(from_val);
// Ratio::<i32>::from_u32(from_val);
// Ratio::<u16>::from_u32(from_val);
// Ratio::<i16>::from_u32(from_val);
}
for i in unsafe {mem::transmute::<_, u64>((u64::MAX - 100_000_000) as f64) }..=u64::MAX {
let from_val = unsafe { mem::transmute(i) };
// Problems (aborted early):
// - `18446744073709552000_f64` (385 above `u64::MAX`)
// Ratio::<u64>::from_f64(from_val);
}
for i in unsafe {mem::transmute::<_, u64>((i64::MAX - 100_000_000) as f64) }..=u64::MAX {
let from_val = unsafe { mem::transmute(i) };
// Problems:
// - `9223372036854776000_f64` (193 above `i64::MAX`)
// - `-9223372036854776000_f64` (192 below `i64::MIN`)
Ratio::<i64>::from_f64(from_val);
}
// No problems until early abortion.
for i in unsafe {mem::transmute::<_, u64>((u32::MAX - 100_000_000) as f64) }..=u64::MAX {
let from_val = unsafe { mem::transmute(i) };
Ratio::<u32>::from_f64(from_val);
}
for i in unsafe {mem::transmute::<_, u64>((i32::MAX - 100_000_000) as f64) }..=u64::MAX {
let from_val = unsafe { mem::transmute(i) };
Ratio::<i32>::from_f64(from_val);
} ( As shown in the code, I'm not sure why the other cases aren't caught at the top of the function where the range is checked. |
Yes, the standard library formatting tries to use the least number of significant digits that will still parse back to the same floating point value. Since we're only concerned with integers here, you can format
That's correct, because So I think what you've demonstrated is that the earlier However, the other side around |
I wouldn't work on this. Do you want a dedicated issue for it? |
I use this crate to convert an
f64
into an fps value (video framerate) consisting ofu32
numerator and denominator. The documentation forRatio::<u32>::from_f64()
, forRatio<T>
and fornum_traits::cast::FromPrimitive
is not clear enough about what exactly it means that the input value can't "be represented by" a ratio.I read your crate's
approximate_float_unsigned()
source code and, based on it, wrote the following in the doc comment of my crate'sFps::from_f64()
:In other cases than mine like
Ratio::<u64>::from_f32()
, your function's linelet t_max_f = <F as NumCast>::from(t_max.clone())?;
may also returnNone
.I'd like to be able to be forever sure about when exactly
None
can be returned without the necessity of "according to".Questions/suggestions:
0
exclusively be yielded when the inputf64
is positive/negative infinity?None
can be returned? (Possibly in the documentation forRatio<T>
with references to it in the other locations.)The text was updated successfully, but these errors were encountered: