Proposal: fixing the 'pure' floating point problem.

Don nospam at nospam.com
Fri Mar 13 03:52:08 PDT 2009


Consider this code:
double add(double x, double y) {
     return x + y;
}
Is this code pure? Is it nothrow?
Technically, it isn't. If you change the floating-point rounding mode on 
the processor, you get a different result for exactly the same inputs. 
If you change the floating-point traps, it could throw a floating-point 
overflow exception.

One Draconian solution would be to say that this code is NOT pure. This 
would mean that floating-point could not be used _at all_ in pure code. 
Therefore, a template function like this could probably not be pure:
T add(T)(T x, T y) { return x + y; }
And everything unravels from there.

Another solution would be to simply ignore the floating point flags, and 
mark the relevant functions as pure anyway.  That would be a shame -- 
DMD goes to a lot of trouble to ensure that they remain valid (which is 
one of the reasons why DMD's floating point code is so slow). We'd be 
well behind C99 and C++ in support for IEEE floating-point. I don't like 
that much.

--- A solution ---

Extend the parametrized module declaration to include something like
module(system, floatingpoint)
as well as
module(system).

This would indicate that the module is floating-point aware. Every 
function in that module has two implicit inout parameters: the floating 
point status and control registers. This matters ONLY if the compiler 
chooses to cache the result of any function in that module which is 
marked as 'pure', it must also check the floating-point status and 
control, if the function is called from inside any floatingpoint module. 
(Most likely, the compiler would simply not bother caching purity of 
floating-point modules). This ensures purity is preserved, _and_ the 
advanced floating point features remain available.

Functions inside a floating-point aware module would behave exactly as 
they do in normal D.

And now comes the big win. The compiler knows that if a module is not 
"floatingpoint", the status flags and rounding DO NOT MATTER. It can 
assume the floating-point default situation (round-to-nearest, no 
floating point exceptions activated). This allows the compiler to 
optimize more aggressively. Of course, it is not required to do so.

Note: The compiler can actually cache calls to pure functions defined in 
"floatingpoint" modules in the normal way, since even though the 
functions care about advanced features, the code calling those functions 
isn't interested. But I doubt the compiler would bother.

This proposal is a little similar to the "Borneo" programming language 
proposal for Java,
http://sonic.net/~jddarcy/Borneo/
which was made by one of William Kahan's students. He proposed 
annotating every function, specifying which floating point exceptions it 
may read or write. In my opinion, that's massive overkill -- it's only 
in a few situations that anyone cares about this stuff, most of the 
time, you don't. And even when you do care about it, it will only be in 
small number of modules.

Since DMD doesn't cache pure functions, it doesn't require any changes 
to support this (other than the module statement).

BTW, module(floatingpoint) is just a suggestion. I'm sure someone could 
come up with a better term. It could even be really verbose, it's hardly 
ever going to be used.

Don.



More information about the Digitalmars-d mailing list