Make signed zero treatment deterministic in min and max (and clamp)
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
Currently, the behavior of min and max (and clamp) is non-deterministic when signed zeros are involved. Non-determinism can be a significant footgun when it is unexpected as it often (and in particular in this case) means that behavior depends on the target and/or optimization level.
So, it seem worth considering changing that. In the words of @jyknight
I would strongly suggest that Rust should tweak the specification of these functions to eliminate the non-determinism in the sign of zero. IMHO, that was a historical mistake in C's fmin/fmax which nobody else should be copying.
The sign of zero is important for various mathematical floating-point algorithms, and having non-determinism in its sign, especially from a fundamental operation, is just weird. I don't think it can be justified other than compatibility with existing implementations. Even the C standard says these days, albeit in a non-binding footnote, "If possible, fmax is sensitive to the sign of zero, for example fmax(−0.0, +0.0) ideally returns +0."
The reason why the sign of zero is important, generally, in IEEE floating-point math is that the values are not Real numbers. So, -0 may represent "a value close to but less than zero", vs +0 may represent "a value close to but greater than zero". The distinction is useful, then, for operations that are discontiguous at zero, such as f(x) = 1/x, where in floating-point math,f(0) = Inf and f(-0) = -Inf. [...]
It's even more important in complex math, when dealing with branch cuts for a multi-valued operation such as sqrt. It turns out to be rather irritating if Complex::new(-4.0, -5.0e-324).sqrt(); ends up in a different quadrant of the complex plane than Complex::new(-4.0, -5.0e-324 / 2.0).sqrt(); (which is what would happen if the division in the latter rounded to 0.0 instead of -0.0). The widely-cited paper on all of this is William Kahan's "Branch cuts for complex elementary functions OR Much Ado About Nothing's Sign Bit".
The main reason why the treatment of signed zeros is left non-deterministic is to improve codegen on x86, which has no native support for float min/max (except for recent extensions that aren't sufficiently widely available yet). My understanding is that guaranteeing -0.0 < +0.0 for the purpose of min/max would require a few extra instructions compared to the current lowering, and t-libs-api considers that cost too high (but I haven't seen that cost actually measured). Personally, I think we should generally err on the side of defaulting to predictable portable behavior, but the performance hit of that choice needs to be tolerable (and the threshold for that is obviously subjective).
So... I think to make a case towards t-libs-api that this behavior is worth changing, someone needs to do some hacking and benchmarking to quantify the actual performance loss that reliable signed zero handling would incur on x86. I don't plan to do any follow-up work here myself, this is far outside my expertise anyway. I leave this issue as a place to track any further efforts people might want to undertake in tweaking this particular aspect of Rust. :)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
The issue names no files or tests; start by measuring current x86 code generation for min, max, and clamp with signed zeros, then compare it with deterministic handling. Document the performance cost across relevant optimization levels and targets so t-libs-api can evaluate whether the predictable behavior is acceptable.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers, performance
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100