Type concerns about scaled(a, m) (conversion/mixed precision; composition of scaled())
Nobody has claimed this yet.
- Dominant language
- HTML
- Stars
- 29
- Forks
- 26
- PR merge metrics
- No merged PRs in 30d
Description
Conversation with @mhoemmen where I initially brought this up:
Phil Miller 6:36 PM
There’s an interesting quirk to the folding of nested scaled applications - the product of the scaling factors will be computed in a different type than the underlying right-associated multiplication against theElementTypewould produce. I think this is mostly fine and good, but I can come up with at least one example where this can lead to bad behavior. Considermdspan<double,…> m; scaled(1<<20, scaled(1 << 20, m))- theProductScalingFactorisint, and its value will overflow, yielding UB, rather than scaling by 2^40 as it would appear it should, and readily could if the calculation were in double . Even unsigned1u << 20in both instances would produce wrong results, though not UB.Mark Hoemmen 8:44 PM
[snip] We definitely need to give those proxy references more attention in the proposal. I would ideally like those expressions to behave as if they were typed out by hand in a for loop.Phil Miller 10:35 PM
I think the wording as written gets you the behavior “as if they were typed out by hand in a for loop” - the multiplication will be left-associated, and so the product of all the scaling factors will be formed in whatever type they promote/convert to before they touch the element type. It’s not unreasonable that multiplication by a stack of integer factors that ultimately overflow anintshould be something for users to avoid.
It feels weird to me, I think, because thescaled(b, scaled(a, m))expression seems to naturally correspond to a right-associated explicit parenthesization of the same - e.g.(b * (a * m))
I just thought of another case that would potentially suffer from this, just barely less contrived. Consider a painfully naive LU factorization, that operates element-by-element rather than in blocks. With each valuedalong the diagonal, the remainder of the matrix will be scaled by its reciprocal1/d. The product of those reciprocals could go subnormal or underflow, even if the values being scaled would be fine as actually divided step-wise. This doesn’t involve any type conversions, just the quirk of evaluation order.
The commonality between these is that multiple layers of scaled may be composed in settings where the product of the scaling factors runs up against numeric representation limits, where the value being scaled would not.Mark Hoemmen 7:26 AM
"Multiple layers ofscaled" is perhaps the key phrase here. The expression templates were Christian's idea, to reduce the number of parameters that BLAS functions have to take, and to make it possible to expressalpha * xwithout needing users to supply a1.0. It's a good idea, but it's caused a bit of divergence from the BLAS (P1673R8 has a discussion about this with regards toTriangle). The idea was always thatscaledwas a way to express setting a scaling factor, and that multiple instances ofscaledin an expression are an antipattern. I'm more pessimistic about users' willingness to respect design intent.
It would be reasonable to expectscaled(b, scaled(a, m))to parenthesize likeb * (a * m[k]).Mark Hoemmen
9:25
I'm more pessimistic about users' willingness to respect design intent.Phil Miller 9:25 AM
In any case, I think even with mis-use, actual problems resulting from it will be rare
Lots of things have to go wrong together for it to work out badlyMark Hoemmen 9:26 AM
C++ Standard Library design is a bit more like nuclear power plant control system design than library designPhil Miller 9:30 AM
Eh, there are plenty of places where it’s perfectly well accepted that misuse can lead to things going wrong. NPP design review doesn’t really accept “results in undefined behavior”Mark Hoemmen 9:31 AM
true -- on the other hand, i would not likescaled(b, scaled(a, x))to be UB ; - )Phil Miller 9:32 AM
The examples I’ve come up with have all been pretty extreme corner cases. The common cases of a few static factors or reasonable numbers from input data should be fine
And it’s all limited by only mattering when the left-association breaks, but the right-association would be fine
Something to think about, but my inclination is that it should probably be noted as a hazard, and left as is, because it is an important optimization
Contributor guide
No contributing guide indexed for this repository
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
Start by reviewing the discussion about nested scaled() expressions and the P1673R8 discussion mentioned in the issue. Determine whether the proposal should change or document the evaluation and conversion behavior; done requires an explicit resolution for composed scaling factors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100