Introducing `mozilla/FloatingPoint.h`: methods for floating point types and values

Tags: , , , , , , — Jeff @ 19:00

The latest addition to the Mozilla Framework Based on Templates (mfbt) is `mozilla/FloatingPoint.h`. This header implements various floating point functionality.

Functionality overview

`mozilla/FloatingPoint.h` currently implements the following functionality, all centered around working with double-precision floating point numbers. (There’s no single-precision support only because Mozilla seemingly doesn’t need it. The only code I can find that does this sort of thing for single-precision numbers is `nsCoord.h`, and that only barely. We can add single-precision equivalents when we need them.)

`MOZ_DOUBLE_IS_NaN(double d)`
Determines whether a value is `NaN` (not a number).
`MOZ_DOUBLE_IS_INFINITE(double d)`
Determines whether a value is positive or negative infinity.
`MOZ_DOUBLE_IS_FINITE(double d)`
Determines whether a number is finite — that is, not `NaN` or positive or negative infinity.
`MOZ_DOUBLE_IS_NEGATIVE(double d)`
Determines whether a number is negative. This is useful because `d < 0` does not answer this question! There are two zero values, `+0` and `-0`, and IEEE-754 requires that `(-0 < 0)` be false. (There are good reasons for this, but this isn’t the place to get into them. If you haven’t read it, read What Every Computer Scientist Should Know About Floating-Point Arithmetic right now. It probably gives the answer, and much much more knowledge as well.) This method properly distinguishes `-0` as being negative.
`MOZ_DOUBLE_IS_NEGATIVE_ZERO(double d)`
Determines whether a number is `-0`.
`MOZ_DOUBLE_EXPONENT(double d)`

Returns the exponent portion of the number. Floating point numbers are represented as a sign bit `s`, a binary fraction `b0..p`, and an exponent `E`. The represented number, then, is `(-1)s(b0..p)2E`. This method returns the number `E` for a floating point number.
`MOZ_DOUBLE_POSITIVE_INFINITY()`
Returns positive infinity.
`MOZ_DOUBLE_NEGATIVE_INFINITY()`
Returns negative infinity.
`MOZ_DOUBLE_SPECIFIC_NaN(int signbit, uint64_t significand)`
Computes a specific `NaN` value, with a bit pattern specified by provided parameters. The bit layout IEEE-754 specifies for floating point formats interestingly requires that multiple bit patterns be treated as `NaN` values. This method allows the user to create such custom `NaN` values if he needs to. (99% of code should never, ever touch this method. Instead, most code should use…)
`MOZ_DOUBLE_NaN()`

Computes an unspecified `NaN` value. If you need a `NaN` value and you don’t know that you need a specific `NaN`, use this method instead to get one.
`MOZ_DOUBLE_MIN_VALUE()`
Returns the smallest non-zero positive double value.
`MOZ_DOUBLE_IS_INT32(double d, int32_t* i)`
Determines if the provided number is a signed 32-bit integer. (`-0` doesn’t count as such; `+0`, the “normal” zero value, does.) If it is, `*i` will be set to that value when the method returns.

(There’s one more method in the header, currently, that used to have users. Sometime in the last couple months, however, that method’s users all disappeared, and I didn’t notice it when rebasing. Thus I’ll be removing it shortly, and I haven’t mentioned it here.)

Why add some of these methods? Aren’t `isinf`, `isnan`, and so on good enough?

There are standard methods implementing some (but not all) of this functionality. In the best of all possible worlds we could simply use `isnan` and other such methods directly. In practice we’ve encountered a number of problems.

First, Microsoft’s compilers gratuitously Think Different and don’t expose `isnan` and friends, so on those platforms you’d have to use `_isnan` instead. (`std::isnan` isn’t usable there because some of our code still must work as both C and C++.) Obviously, we don’t want to `#ifdef` every place we need to use the method.

Second, we’ve found various compilers have bugs when using either the standard methods or Microsoft’s bogo-named methods. Most commonly this bustage occurs with PGO builds; interestingly, both MSVC and gcc have problems here, despite their optimizers obviously not sharing any code.

Third, we’ve found even some obvious bitwise algorithms trigger PGO build failures, again on entirely different compilers. (You can’t win.)

Basically, then, we can’t use the standard methods, we can’t use some bitwise methods, and whatever we do we have to be really careful about to make sure we don’t break anything. Hopefully this header will satisfy those requirements.

The header is located at `mfbt/FloatingPoint.h` in the source tree. However, per standard mfbt practice, you should use `#include "mozilla/FloatingPoint.h"` to include it. Knock yourself out using it.

Party like it’s 1999: `<stdint.h>` comes to Mozilla!

tl;dr

Need an integer type with guaranteed size? If you’re not defining a cross-file interface, `#include "mozilla/StdInt.h"``#include "mozilla/StandardInteger.h"` and use `uint32_t` or any other type defined by `<stdint.h>`. (If you are defining an interface, use `PRUint32` and similar — for now.) `mozilla/StdInt.h``mozilla/StandardInteger.h` is a cross-platform implementation of `<stdint.h>`‘s functionality usable in any code.

Embedders may find that the `mozilla/StdInt.h``mozilla/StandardInteger.h` `typedef`s conflict with ones they have already been using. To work around this conflict, write a `stdint.h` compatible with the embedding’s `typedef`s (more likely: adapt an existing implementation), then set the preprocessor variable `MOZ_CUSTOM_STDINT_H` to a quoted path to that reimplementation when `mozilla/StdInt.h``mozilla/StandardInteger.h` is included. It may be simplest to add this to command line flags when invoking the compiler.

Fixed-size integer types

Fixed-size integer types are signed or unsigned types with exactly N bits. They contrast with the built-in C and C++ types (`char`, `short`, `int`, &c.) with compiler-dependent sizes. Fixed-size integer types are quite useful:

• They work well when serializing an object to a sequence of bytes, where the size of a serialized item must be constant for correctness.
• They minimize memory use in `class`es and `struct`s, also making padding-based waste more obvious.
• They work well in cross-platform APIs, eliminating the challenge of implementing correct behavior when types have different sizes across platforms.

Fixed-size integer types are useful in the same way `size_t`, `off_t`, and other non-built-in types are: they fit some problem domains better than built-in types.

C99 and C++11 finally standardized fixed-size integer types in `<stdint.h>` and `<cstdint>`. They define `{u,}int{8,16,32,64}_t` types, plus useful constants for their limits (`INT8_MIN`, `INT8_MAX`, `UINT8_MAX`, `INT16_MIN`, &c.). One would expect projects to quickly use these types, but it didn’t happen.

Old projects predating `<stdint.h>` have been particularly slow to adopt it. Many such projects already rolled their own non-`<stdint.h>`-named fixed-size integer types; switching would be a hassle. And not all compilers shipped `<stdint.h>`: Visual Studio didn’t have it until 2010! (ಠ_ಠ) Projects implementing their own `<stdint.h>`-compatible types posed another problem, because different projects’ implementations might be incompatible.

New projects fare better, but not always. Sometimes their dependence on old projects anchors them to the old, pre-`<stdint.h>` world.

Fixed-size integer types in Mozilla

Mozilla sits squarely in the old-project category, facing every rationale noted above for using its own types. These have long been NSPR‘s `PR{Ui,I}nt{8,16,32,64}` and SpiderMonkey’s `{u,}int{8,16,32,64}` types. But recently the landscape has changed.

As Mozilla has imported more external code, fixed-size integer types have proliferated. Most imported code uses `<stdint.h>`, with fallback `typedef`s for Visual Studio; some code (IPC code from Chromium) defines and uses `{u,}int{8,16,32,64}`. As type definitions have proliferated, surprising problems have arisen.

The woes of multiplicity

`#include`-order issues are the simplest problem. For example, the IPC `uint32`-style definitions are incompatible with SpiderMonkey’s definitions with some compilers. `#include` IPC and SpiderMonkey headers in the wrong order, and the compiler will error on incompatible `typedef`s. This problem is easy to diagnose but harder to resolve, and sometimes it causes considerable pain. Mass refactorings that add `#include`s have fallen afoul of this, with the least bad solution usually being to fix the first error, recompile, and repeat until done (twenty-odd times in one instance).

Worse than `#include` mis-ordering and conflict are linking problems. `int` and `long` could be 32-bit integers yet appear different when linked. Suppose a method taking an `int32` argument defined `int32 = int` during compilation, but a user of it saw `int32 = long` during compilation. Each alone would compile. But beneath `typedef`s they’d be incompatible and wouldn’t link.

As we’ve imported more code in Mozilla, more and more developers have been bitten by these problems. We’ve reached a breaking point. We could use `PRUint32`, `JSUint32`, and other types which never trample upon each other. Yet no one likes them given the standardized types, and it’s not possible to change “upstream” code to such a scheme. Thus a second solution: use `<stdint.h>` definitions for everything.

Switching to `<stdint.h>`

Using the `<stdint.h>` types in Mozilla code is now as simple as `#include "mozilla/StdInt.h"``#include "mozilla/StandardInteger.h"`. `mozilla/StdInt.h``mozilla/StandardInteger.h` implements the `<stdint.h>` interface even in the edge cases: for compilers not supporting it, and for embedders who can’t use the regular definitions. It works as follows:

1. If the preprocessor definition `MOZ_CUSTOM_STDINT_H` is defined, then `#include MOZ_CUSTOM_STDINT_H`. Embedders who can’t use the default definitions should use this to adapt. (`MOZ_CUSTOM_STDINT_H` may also be passed into the Mozilla build system using an environment variable. Note that while the preprocessor definition must be a quoted path, the environment variable must be an unquoted path.)
2. Otherwise, if the compiler doesn’t provide `<stdint.h>`, use a custom implementation. This is currently limited to Visual Studio prior to 2010, using an implementation imported from msinttypes on Google Code.
3. Otherwise use `<stdint.h>`.

We’re only providing these types now, but shortly we’ll start switching code using non-`<stdint.h>` fixed-size integer types to use them. Adding `mozilla/StdInt.h``mozilla/StandardInteger.h` is merely the first step toward removing the other fixed-size integer types (except when they’re necessary to interact with external libraries).

Conclusion

It’s been a dozen years since `<stdint.h>` was standardized. Now it finally comes to Mozilla. Let’s lower a barrier to hackability in Mozilla and start using it.