Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upWrapper newtypes for f32 and f64 implementing Ord #1249
Comments
This comment has been minimized.
This comment has been minimized.
|
I swear we had some sort of issue like this somewhere, but cannot find it. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
nixpulvis
commented
Aug 26, 2015
|
Could this be the right place to implement a |
This comment has been minimized.
This comment has been minimized.
oberien
commented
Aug 26, 2015
|
+5000 ;) It's just such a common thing to compare floats, where NaN and Infinity are not a problem. But you still need to implement custom Wrappers which IMHO just sucks... |
This comment has been minimized.
This comment has been minimized.
I think this is the key. There's nothing stopping someone from doing this right now and starting to gather feedback on how much it is used. That, in turn, could help suggest the importance of including it in the standard library. |
nrc
added
the
T-libs
label
Aug 25, 2016
This comment has been minimized.
This comment has been minimized.
jfager
commented
Dec 14, 2016
In IEEE-754 the lexicographical ordering of the bytes is basically the ordering of the representable floating-point values, with only some minor bit-twiddling required. I have some old java code that does this, and there are a few other implementations online (for instance, https://www.working-software.com/cpp-float-comparisons). I have a good citation explaining the details on my home machine, will post when I get back. |
This comment has been minimized.
This comment has been minimized.
coriolinus
commented
Nov 8, 2017
|
Is this issue solved by the |
This comment has been minimized.
This comment has been minimized.
jpetkau
commented
Feb 26, 2018
•
|
@coriolinus noisy_float does something different: it prevents NaNs in debug builds via copious debug_asserts, and assumes (without checking) that NaNs do not occur in release builds so that it can implement Ord as a weak ordering, i.e. a total ordering with +0 == -0 comparing equal. I guess that technically solves the issue as described in the title, but not some of the issues that have been folded into it (e.g. #1367), which want totally-ordered wrappers without overhead.
As @jfager pointed out, floats are fortunately much more well-specified than that. Interpreting the bits as a signed integer gives the correct ordering for positive floats, and the reverse ordering for negative floats. But you can do even better: IEEE754-2008 defined a totalorder predicate, which gives a strong ordering (i.e. equality under the ordering implies bitwise equality). An implementation can be as simple as: fn f32_bits(a: f32) -> i32 { unsafe { std::mem::transmute(a) } }
fn cmp_total_order(a: f32, b: f32) -> std::cmp::Ordering {
// ideally this would be replaced by a totalorder primitive when that's available
let mut a = f32_bits(a);
let mut b = f32_bits(b);
if a < 0 { a ^= 0x7fffffff; }
if b < 0 { b ^= 0x7fffffff; }
a.cmp(&b)
}There's an abstract argument why this should be added (better IEEE754-2008 conformance), but it's also much more useful in practice than a partial order. Partial orders violate basic equality axioms like Rust fortunately makes it hard to do the wrong thing since floats don't implement Ord, so you can't easily hit yourself with that footgun, but it would also help to make it easier to do the right thing by having a total ordering available when you need it. |
This comment has been minimized.
This comment has been minimized.
|
Related recent internals discussion: https://internals.rust-lang.org/t/pre-pre-rfc-range-restricting-wrappers-for-floating-point-types/6701 |
glaebhoerl commentedAug 11, 2015
The Rust standard library (rightly, in my view) distinguishes
PartialOrd(resp. -Eq) fromOrd, primarily because according to their usual semantics, IEEE754 floating-point numbers do not have a total ordering, and therefore implement only thePartialtraits. So far, so good. However, beyond this point, when faced with common tasks involving floating-point types which would in fact require an implementation ofEqorOrd- such as sorting, minimum/maximum, binary search, using them as keys in a data structure, etc. - users are currently, to a large degree, left to figure things out on their own, often leading to pain and frustration on their part.We could do something about this, and make their lives easier, by providing wrapper newtypes (single-field
structs) over the floating-point types which do in fact implementEqandOrd, according to various policies. I can think of several different possibilities off the top of my head, any of which may be useful:EqandOrdon this basis. (This is based on a suggestion by @shepmaster in an answer on StackOverflow.)u32andu64. This ordering is likely to bear no relationship to the numerical one, but may be appropriate if one wishes to use floating-point types as keys in a data structure, provided that it doesn't really matter what the ordering (resp. equality) is, as long as it's total and fast.If these were available, the options for bridging the gap between
f{32,64} andOrdwould be more apparent to users, and they could select the appropriate policy for dealing with NaNs based on their needs.(Of course, such functionality would likely benefit from prototyping on crates.io beforehand, but I don't think it's unreasonable to consider it a candidate for eventual inclusion in
std.)