Bugzilla – Bug 16075
Make a float/double type that excludes NaN/Infinity/-Infinity
Last modified: 2012-03-12 20:50:56 UTC
float/double can be NaN/Infinity/-Infinity. This is probably not desired by most if not all interfaces that use them. The uses of float/double in HTML are the following, as far as I can tell, excluding readonly attributes and return types (since behavior there doesn't really matter):
* HTMLMediaElement and MediaController: .currentTime, .playbackRate, .defaultPlaybackRate, .volume
* TextTrackCue: constructor, .startTime and .endTime
* CanvasRenderingContext2D: scale(), rotate(), translate(), transform(), setTransform(), .globalAlpha, about a million others
* HTMLInputElement: .valueAsNumber
* HTMLProgressElement: .value, .max
* HTMLMeterElement: .value, .min, .max, .low, .high, .optimum
The CSS Transforms spec also currently uses doubles for CSSMatrix (see bug 15964), and SVG defines some interfaces like that as well (SVGPoint, SVGMatrix, etc.).
In all of these cases, as far as I can tell, NaN and infinite values are unlikely to be wanted. Moreover, looking at a sampling of them, they seem to not define handling for those cases. It is entirely unclear what should happen if you set a video element's .currentTime to NaN or Infinity, for instance, or if you try to scale() a canvas by a factor of NaN or Infinity. The spec doesn't say.
I suggest that the IDL float and double types be defined to exclude NaN, Infinity, and -Infinity values. Trying to convert such a value to float or double should raise a TypeError. If there are any WebIDL users that actually want to allow such values, it should be via an extended attribute that lets them opt in.
zewt suggests that they should be converted to +0, rather than raising a TypeError. This matches the behavior of integer types, AFAICT, so it probably makes more sense. (A priori, I'd have said throwing is more reasonable, but it's better to match integer types.)
Ms2ger points out that HTML already defines that a NotSupportedError has to be thrown in these cases:
Except for canvas, where it's silently ignored:
The silent-ignore behavior for canvas needs to be supported somehow. I definitely think the default handling of throwing an exception should be moved to WebIDL (and changed to TypeError for consistency).
I can see that it would be useful to distinguish between float/double values that take non-finite values and ones that don't in the IDL. Rather than an extended attribute, I think it would be good to just have a separate type to distinguish them.
On the one hand, to me "float" and "double" really sound like the C types, where you can have NaNs and ±Infinity. So removing those values by default from those types doesn't seem right. Thus my first thought would be to have maybe "float" and "finite float" types. OTOH, it's clear that not including those values is more common, so littering most uses of "float" with "finite" is maybe not the best solution. If there is a succinct word we could place in front of "float" to mean "including the non-finite values" I can't think of it right now. :)
(Silently ignoring like on canvas calls could be done with an extended attribute or just by using the type that includes the non-finite values and defining the canvas operations to do nothing when those values are passed in.)
A crazy thought: define special types to represent NaN and ±Infinity and use union types when you want them:
void f((float or NaN) x);
"expanded float" / "inclusive float" / "infinity float"
Do we know of any cases where specs actually want to allow NaN or +-Infinity? If not, can we just not allow them at all for now? Failing that, the union type suggestion sounds best -- it's ugly, but that's fine if there are no known uses.
I went with removing the non-finite values from float and double, and introducing two new types "unrestricted float" and "unrestricted double" which do have those values. I noted these new types as being "at risk", in case we don't have any uses for them. When passing non-finite values to an object expecting a float/double, a TypeError is thrown. I haven't added an extended attribute for the "ignore the function call instead of throwing" for canvas; I think we can just have those the relevant functions take "unrestricted float"s and keep defining their special behaviour in those cases.
Let me know if this resolution is satisfactory, thanks.
x = new Date("foo");
(In reply to comment #7)
> "invalid date". This happens for example for
> x = new Date("foo");
Possibly, although Dates are used far less often than floats/doubles so I'm not sure whether it's worth it at the moment. There are only two uses of Date that are taken as input that I could find: HTMLInputElement.valueAsDate and Contact.birthday (from the Contacts API spec). The former handles NaN dates specifically to do something other than throwing. The latter doesn't say what it means for the birthday to be the invalid date value, but it also doesn't have much detail there.
Wouldn't it be better to not throw for NaN / infinity so <canvas> does not need special casing? It seems nicer if people make a math mistake now and then to just let it slide and not do anything with the method. Maybe put a warning in the console or something.
I don't really like the pattern of ignoring a whole function call because one of the float/double arguments got a non-finite value. It's trickier still when the operation is declared with a return type other than void -- you need to define what gets returned from it.
Maybe Aryeh's suggestion of mapping NaN/±Infinity to 0 would be OK, but I would like to see that we're all in favour of that change before making it.
I think having either of those behaviors as a default behavior for float seems very scary. It might make sense for some APIs, but very little for others.
I think we should make the default policy to throw, or just revert this whole bug and force specs to define behavior on a function by function basis.
Yes I tend agree with Jonas regarding throwing being better, but I think the introduction of the "unrestricted" varieties of float and double are useful and shouldn't be rolled back. They give us common behaviour -- for cases that are very common -- without having to rely on all spec writers to specify it.