15:52
<shu>
question that came up internally recently: does anyone have any idea why in https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-math.hypot, the infinite special case takes precedence NaN special case?
16:56
<ljharb>

shu: seems like it may have been a precision concern? (the november 2013 notes in particular)

18:03
<shu>
hm i don't follow the precision argument for infinity vs nan precedence
18:03
<shu>
thanks for digging those up
18:03
<shu>
i see a nan mention in the sept-17 one
18:13
<ljharb>
yeah it's not clear to me from the notes either
18:13
<ljharb>
but that's all i could find
19:02
<TabAtkins>
In several of the math functions (but, annoyingly, not quite all), they'll treat NaN as if it could be every value, and if this still results in a well-defined answer, return that answer instead of returning NaN. That's the case for hypot() - if there's an infinite term, then it doesn't matter what the rest of the terms are, it's gonna return infinity.
19:02
<TabAtkins>
(I did some exploration of this while writing the CSS math functions. I chose not to perfectly match JS in this regard, and instead make NaN immediately infectious, because JS wasn't self-consistent anyway.)
19:03
<TabAtkins>
(In particular, min/max don't follow these rules. Math.max(Infinity, NaN) returns NaN, despite the fact that every possible numeric value the second argument could take would still result in Infinity being returned.)
22:11
<shu>
In several of the math functions (but, annoyingly, not quite all), they'll treat NaN as if it could be every value, and if this still results in a well-defined answer, return that answer instead of returning NaN. That's the case for hypot() - if there's an infinite term, then it doesn't matter what the rest of the terms are, it's gonna return infinity.
the question is why Infinity has precedence over NaN
22:11
<shu>
if you wrote that code in userland JS, NaN would have precedence over Infinity
22:11
<shu>
wrote it in the naive way, i guess
22:12
<TabAtkins>
Yeah, I said why - in several functions, they allow a non-NaN answer if NaN could be replaced with anything without changing the result.
22:13
<TabAtkins>
Which is relatively reasonable behavior, I think. (But the other behavior - fully infectious NaN - is also reasonable.)
22:13
<shu>
trying to parse that sentence...
22:14
<shu>
okay sorry i just don't understand the precedence
22:15
<shu>
okay, so the precedent is that several functions already decided that NaN should have lower precedence than Infinity because Infinity is thought of as just another "non-NaN"?
22:16
<shu>
that doesn't give me insight on why they made this decision when the precedence in user + is the opposite
22:18
<TabAtkins>
No, the (mixed!) precedent is that, in several functions, if the NaN argument doesn't affect the result (aka you could replace it with any non-NaN value, and every possible substitution would have no effect on the return value), then the function just returns that consistent value.
22:18
<TabAtkins>
It's not a "lower precedence" thing, it's a "the return value doesn't depend on this argument, so we won't pay any attention to it" thing.
22:19
<TabAtkins>
Infinity is just another number; NaN is the weird one that (among other reasons) is returned when multiple possible values are possible.
22:23
<TabAtkins>
+ is consistent with this behavior: in Infinity + X, if X=-Infinity you get NaN, while any other value yields Infinity, so the X's value is important to the result, so Infinity + NaN has to be NaN too.
22:30
<shu>
Infinity + NaN is NaN, right
22:30
<shu>
but hypot(Infinity, NaN) is Infinity?
22:31
<shu>
that's the inconsistency, no?
22:34
<TabAtkins>
No, substitute any non-NaN value for the second argument there. You'll always get Infinity, still.
22:35
<TabAtkins>
So the NaN argument is powerless, and can be safely ignored.
22:36
<TabAtkins>
(hypot(Infinity, Infinity), hypot(Infinity, 0), hypot(Infinity, -Infinity) all give Infinity; once one argument is infinite the entire function is guaranteed to return Infinity regardless of the other values.)
22:36
<shu>
i think we're talking past each other somehow
22:37
<TabAtkins>
We must be, since you still seem to be talking about this in terms of "precedence", like the existence of an Infinity vs a NaN does something and we have to define which one "wins" when they both appear.
22:37
<TabAtkins>
What I'm explaining is that the behavior isn't about that at all.
22:38
<shu>
looking at just +, Infinity + NaN, one cannot know whether the rule that applies is "Infinity is infectious" or "NaN is infectious", and people just internalize it as "NaN is infectious" applies over "Infinity is infectious"
22:38
<shu>
in that Infinity + NaN is a degenerate form that has no intuition in extended real arithmetic
22:38
<TabAtkins>
Sure. But "Infinity + NaN yields NaN" is also consistent with the reasoning I gave, which appears to be the reasoning used by hypot().
22:39
<TabAtkins>
(But not the reasoning used by min()/max(); they both use "NaN is infectious" logic.)
22:39
<shu>
look at hypot(Infinity, NaN), one also cannot know whether the rule that applies is "Infinity is infectious" or "NaN is infectious", and people just internalize one, except it's confusing now because they need to internalize the other one
22:39
<TabAtkins>
yeah, the logic isn't about infection in hypot()
22:39
<TabAtkins>
is what i keep saying
22:39
<shu>
okay, think i finally see where you're coming from
22:40
<shu>
but doesn't that kick the can one level up? there's still inconsistent in which logic is used for which operations/functions, and it's confusing
22:40
<TabAtkins>
Yes, it's inconsistent no matter what reasoning you use
22:40
<TabAtkins>
our functions are just straight up inconsistent in their NaN handling
22:41
<shu>
okay, bear with me, but now we're back at my still not having any insight on how we arrived at this inconsistency in logic
22:41
<shu>
i guess just accident of how things were argued at the time
22:48
<TabAtkins>
yup