Skip to content
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

[geometry-1] Is a specific handling for Infinity implied by "NaN-safe minimum/maximum"? #556

Open
towerofnix opened this issue Mar 9, 2024 · 1 comment

Comments

@towerofnix
Copy link

Document conventions includes the following description for NaN-safe minimum and NaN-safe maximum:

The NaN-safe minimum of a non-empty list of unrestricted double values is NaN if any member of the list is NaN, or the minimum of the list otherwise.

Analogously, the NaN-safe maximum of a non-empty list of unrestricted double values is NaN if any member of the list is NaN, or the maximum of the list otherwise.

This was introduced in #395 following discussion in #222 and mailing lists (quoted in the latter issue).

Although Infinity is explicitly mentioned in the (quoted) mailing list, it isn't in the spec or issue discussion.

On Sat, Oct 15, 2016 at 2:47 PM, Simon Fraser smfr@me.com wrote: (excerpt)
Using unrestricted doubles forces implementors to handle NaN and Inf values for x, y, width and height, and to correctly propagate NaN and Inf through steps used to calculate left, top, right, bottom (assuming IEEE rules, though the spec does not make this explicit). […] Is the complexity worth it?

On Oct 16, 2016, at 9:34 AM, Rik Cabanier cabanier@gmail.com wrote: (excerpt, emphasis mine)
yes. We allowed this so NaN and Inf would signal when matrix calculations hit edge conditions.
Instead of throwing or giving inaccurate result, it was decided to allow the values so authors can check for those. The alternative would be to throw and we feared that this would break a lot of code since people don't test with exceptions. […]

So I just wanted to ask, is the behavior for Infinity currently defined? At the moment we're using the verb "if ... is NaN" to specify the special behavior ("the NaN-safe minimum … is NaN"), and the default behavior is otherwise only "the minimum of" the list of terms. I don't see definitions or references for what "is NaN" and "the minimum of" mean within this spec, which seems like potentially undefined behavior to me.

Test script to observe behavior of infinity on various edges and areas:
rects = {
  infinityTowardsRight: new DOMRect(0, 0, Infinity, 0),
  infinityTowardsLeft: new DOMRect(0, 0, -Infinity, 0),
  infinityAtRight: new DOMRect(Infinity, 0, 0, 0),
  infinityAtLeft: new DOMRect(-Infinity, 0, 0, 0),
  infinityLeftToRight: new DOMRect(-Infinity, 0, Infinity, 0),
  infinityRightToLeft: new DOMRect(Infinity, 0, -Infinity, 0),

  infinityTowardsBottom: new DOMRect(0, 0, 0, Infinity),
  infinityTowardsTop: new DOMRect(0, 0, 0, -Infinity),
  infinityAtBottom: new DOMRect(0, Infinity, 0, 0),
  infinityAtTop: new DOMRect(0, -Infinity, 0, 0),
  infinityTopToBottom: new DOMRect(0, -Infinity, 0, Infinity),
  infinityBottomToTop: new DOMRect(0, Infinity, 0, -Infinity),

  bottomRightQuadrant: new DOMRect(0, 0, Infinity, Infinity),
  bottomLeftQuadrant: new DOMRect(0, 0, -Infinity, Infinity),
  topRightQuadrant: new DOMRect(0, 0, Infinity, -Infinity),
  topLeftQuadrant: new DOMRect(0, 0, -Infinity, Infinity),

  planeTopBottomLeftRight: new DOMRect(-Infinity, -Infinity, Infinity, Infinity),
  planeTopBottomRightLeft: new DOMRect(Infinity, -Infinity, -Infinity, Infinity),
  planeBottomTopLeftRight: new DOMRect(Infinity, -Infinity, Infinity, -Infinity),
  planeBottomTopRightLeft: new DOMRect(Infinity, Infinity, -Infinity, -Infinity),
};

props = ['x', 'y', 'width', 'height', 'left', 'right', 'top', 'bottom'];

console.log(`Rectangle,` + props.join(','));
for (const [name, rect] of Object.entries(rects)) {
  console.log(`${name},${props.map(p => rect[p]).join(',')}`);
}
Results are conveniently identical in Safari 17.4, Firefox 123, Chrome 122:
Rectangle x y width height left right top bottom
infinityTowardsRight 0 0 Infinity 0 0 Infinity 0 0
infinityTowardsLeft 0 0 -Infinity 0 -Infinity 0 0 0
infinityAtRight Infinity 0 0 0 Infinity Infinity 0 0
infinityAtLeft -Infinity 0 0 0 -Infinity -Infinity 0 0
infinityLeftToRight -Infinity 0 Infinity 0 NaN NaN 0 0
infinityRightToLeft Infinity 0 -Infinity 0 NaN NaN 0 0
infinityTowardsBottom 0 0 0 Infinity 0 0 0 Infinity
infinityTowardsTop 0 0 0 -Infinity 0 0 -Infinity 0
infinityAtBottom 0 Infinity 0 0 0 0 Infinity Infinity
infinityAtTop 0 -Infinity 0 0 0 0 -Infinity -Infinity
infinityTopToBottom 0 -Infinity 0 Infinity 0 0 NaN NaN
infinityBottomToTop 0 Infinity 0 -Infinity 0 0 NaN NaN
bottomRightQuadrant 0 0 Infinity Infinity 0 Infinity 0 Infinity
bottomLeftQuadrant 0 0 -Infinity Infinity -Infinity 0 0 Infinity
topRightQuadrant 0 0 Infinity -Infinity 0 Infinity -Infinity 0
topLeftQuadrant 0 0 -Infinity Infinity -Infinity 0 0 Infinity
planeTopBottomLeftRight -Infinity -Infinity Infinity Infinity NaN NaN NaN NaN
planeTopBottomRightLeft Infinity -Infinity -Infinity Infinity NaN NaN NaN NaN
planeBottomTopLeftRight Infinity -Infinity Infinity -Infinity Infinity Infinity -Infinity -Infinity
planeBottomTopRightLeft Infinity Infinity -Infinity -Infinity NaN NaN NaN NaN

The consistent behavior is obviously a good sign, but I'd like to know if the spec is already explicit enough and browsers are strictly following it, or if they've just settled on identical behavior here by happenstance.

Also relevant are the definitions for top/left/right/bottom (bottom of 3. The DOMRect interfaces):

The top attribute, on getting, must return the NaN-safe minimum of the y coordinate and the sum of the y coordinate and the height dimension.

The right attribute, on getting, must return the NaN-safe maximum of the x coordinate and the sum of the x coordinate and the width dimension.

The bottom attribute, on getting, must return the NaN-safe maximum of the y coordinate and the sum of the y coordinate and the height dimension.

The left attribute, on getting, must return the NaN-safe minimum of the x coordinate and the sum of the x coordinate and the width dimension.

Note use of "the sum of".

x, y, width, height are all unrestricted doubles, which "is a floating point numeric type that corresponds to the set of all possible double-precision 64-bit IEEE 754 floating point numbers, finite, non-finite, and special "not a number" values (NaNs)".

I'm not acquainted with IEEE 754 to know if it specifically defines computations for minimum/maximum (which would include details for Infinity, e.g. "the minimum of any set of numbers, not including NaN, and including negative infinity, is negative infinity"), but given the previous concern over NaN propagation being addressed within this spec (rather than externally or implicitly), I'm wondering if Infinity should receive similar treatment.

I also don't know if "is NaN" is explicit enough as-is to imply "is the exact value NaN" (i.e. synonymous with JavaScript Number.isNaN) - I understand it not to imply "infinity is NaN", but this isn't explicit in the spec.

@towerofnix
Copy link
Author

This was kind of an obvious thing for me to overlook initially, but -Infinity + Infinity and Infinity + -Infinity are both NaN, which makes corresponding edges always be NaN: you're getting the minimum/maximum of ±Infinity and NaN, which is always NaN, by spec.

planeBottomTopLeftRight is the only remaining "surprising" case (new DOMRect(Infinity, -Infinity, Infinity, -Infinity)), but here we're summing like signs, (x: Infinity) + (width: Infinity) and (y: -Infinity) + (height: -Infinity), so no NaN is involved and normal minimum/maximum logic applies. This is also, uh, an error in my chart, since really this just means "the top-right quadrant relative to (Infinity, -Infinity)" — the correct plane definition here would be new DOMRect(-Infinity, Infinity, Infinity, -Infinity).

I don't believe it's possible to represent a plane with DOMRect - on a practical level because those involve opposing infinities (so bounds always perform min/max with NaN), and on a theoretical level because planes don't fundamentally express an origin, they're just the dimension space. DOMRect is always in terms of an origin point (a DOMRect could be described as "the positive or negative width/height extending away from an origin x/y").

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant