Proposal: fixing the 'pure' floating point problem.

Don nospam at nospam.com
Tue Apr 14 00:26:14 PDT 2009


Philip Miess wrote:
> Don wrote:
>> Philip Miess wrote:
>>> Walter Bright wrote:
>>>> Don wrote:
>>>>> That's true, but if you're in a floatingpoint module, and you call 
>>>>> a non-floatingpoint module, it's your responsibility to make sure 
>>>>> that the rounding mode is back to normal. You're saying that you 
>>>>> don't care about the status flags. So it's your own fault if you 
>>>>> get surprising results.
>>>>>
>>>>> The primary use for adjusting the rounding mode is for things like 
>>>>> implementing interval arithmetic. Thus, it's only ever used for 
>>>>> small functions.
>>>>
>>>> Perhaps we can go with something simpler. If you call a pure 
>>>> function, then the modes must be set to their defaults.
>>>
>>>
>>> Walter,
>>>     I had a simpler idea.
>>> What about a default rounding mode parameter on pure functions that 
>>> care about the rounding mode.
>>>
>>> like this
>>>
>>> pure int sqrt(int x, invariant roundingMode round = default)
>>> {
>>>     return x*x;
>>> }
>>>
>>> No change to D is necessary to use this.
>>> If you wanted to make it a little easier you could
>>> provide a standard rounding mode class that gets the current rounding 
>>> mode.
>>> now if don't want to use the default rounding mode you pass the 
>>> rounding Mode your using as a parameter.
>>> The function can be cached since it's output only depends on its input.
>>>
>>> In summary, if your pure function depends on the rounding mode that 
>>> should be a parameter of your function.
>>> What do you think, will that work?
>>>
>>> Phil
>>>
>>
>> That's actually a LOT more complicated than my suggestion. Also, it's 
>> not how the rounding modes work.
>>
>> Aargh. It seems that people don't understand that my solution DOES fix 
>> the problem, and is trivial to implement (< 10 lines of code in the 
>> DMD source, one line change in a couple of standard library modules 
>> and THAT'S ALL). Nobody has come up with any problems with it.
>>
>> Especially, I don't think Walter understands my proposal yet.
>>
>> But thanks for replying to the thread, I think it's an important one 
>> to get fixed.
> 
> 
> Don,
>     here is an improved version of my suggestion
> 
> import std.c.fenv;
> import std.math;
> 
> pure long myround(real x, int round = fegetround() )
> {
>     
>      return lrint(x);
> }
> 
> int main(char[][] args)
> {
>     long result;
>     result = myround(2.6);
>     //result is now 3
> 
>     fsetround(FE_DOWNWARD);
>     result = myround(2.6);
>     //result is now 2
> }
> 
> I think that this is easier to understand because everything is explicit 
> and uses the normal syntax.
> Only functions that really use the mode need to be parametrized.
> It requires no change to the compiler, so its simpler for Walter.

Unfortunately, it _does_ require compiler changes. Here are some issues:
(1) The optimiser needs to discard all those calls to fegetround().
(2) It must not display a warning "parameter 'round' is never used".
(3) Every standard math function gets this extra parameter. So taking 
the address of a standard math function will stop working.
(4) The intrinsic functions like sqrt() have signatures which need to 
change.
(5) Properties stop working.
(6) This doesn't deal with the problem of the exception sticky flags.
(7) It doesn't deal with the problem of floating point exception handling.

> It does require a little more typing for the function writer but not much.
> No change is necessary for the use of the function since it 
> automatically picks up the current rounding mode.
> Additionally it does not affect functions that don't need it like your 
> module setting would.
> That way you don't need to segregate all the functions that use the 
> rounding mode into a separate module to avoid penalizing the others.

But if you look at the functions which need it, you'll find they're 
nearly all in the same module anyway (locality of reference). Bear in 
mind that there are very few cases where it is ever used. Originally I 
had thought of marking every function specifically, but I don't think 
that complexity is actually necessary.

> 
> To be clear, I do understand your suggestion and believe it would work.
> I just prefer not to add new elements to the language when there is a
> reasonably good solution already available.

I can only think of one such possible solution[1]. My first proposal was 
the next best thing: the most trivial possible language change, and 
actually giving the compiler additional freedom in exchange.

[1] My second proposal was to provide a runtime call to disable caching 
of pure functions, but that requires every pure function to check a 
global variable to see if caching of pure functions should be ignored.
Unfortunately, this loses many of the optimisation benefits of 'pure', 
and the extra optimisation benefits from my first proposal. So I prefer 
my first proposal. But if you insist on no language changes at all, this 
is the simplest way to do it.



More information about the Digitalmars-d mailing list