chapel-lang / chapel

a Productive Parallel Programming Language
https://chapel-lang.org
Other
1.78k stars 418 forks source link

Math module - should we drop support for nearbyint and rint? #19024

Open lydia-duncan opened 2 years ago

lydia-duncan commented 2 years ago

If in #11970 we decide to not support the C rounding modes, these functions are not really useful. If we do decide to support that particular style of rounding mode, we should camelCase nearbyint to nearbyInt

damianmoz commented 2 years ago

They are not C rounding modes, they are the rounding modes supported by the IEEE 754 standard.

I did not know that #11970 was considering dropping rounding modes and I was part of the conversation. What did I miss and when?

As far as I knew, it was only that there was the possibility that, for now, it is problematic to change rounding modes but that is a compiler problem which needs fixing. Firstly and finally, that problem needs to be addressed longer term. But even without being able to change those modes, these functions are extremely useful in their own right. Specifically, because the default rounding mode is rounding-to-nearest-with-ties-to-even, rint() is the only way to achieve such rounding of a real(w) datum. But that routine may affect the IEEE 754 Exception state. And if you want that functionality but do not want to mess with the Exception state, you still need nearbyint() functionality. So ditching these functions is totally out of the question.

But yes, the name sucks. It is a version of rint() that never affects the INEXACT floating point exception state. When actually, what happens is we capture the state, call rint(), and then if the INEXACT floating point exception was affected, we save it back to what it was before. Both the latest ARM and IBM's System Z support this in hardware. For anything else, see below.

Using a lot of imagination, this routine can be thought of as

proc rintWithoutException(x : ?R) : R
{
    const e = ieee754.testException(ieee754.inexact);
    const t = rint(x);

    if (e != ieee754.inexactException)
    {
        ieee754.voidException(ieee754.inexact);
    }
    return t;
}

The name has not been around long enough for me to object to a change of name over existing practice.

I have used a method on a real(w) called rintNo754X() but that name might be too spartan for some.

lydia-duncan commented 2 years ago

I did not know that #11970 was considering dropping rounding modes and I was part of the conversation. What did I miss and when?

As far as I knew, it was only that there was the possibility that, for now, it is problematic to change rounding modes but that is a compiler problem which needs fixing.

I wouldn't characterize it as dropping them, since we don't really support them much to begin with. It's not just problematic to change rounding modes (though that's definitely a problem) - it's also that we don't even support querying them in code today. You mention:

the default rounding mode is rounding-to-nearest-with-ties-to-even

But that's just something you personally know, and you don't have a way to check if someone else has happened to change it for a particular piece of code you're running - C does provide functions for that, but we don't wrap them today. That's what I mean when I say that nearbyint and rint aren't useful in Chapel code today - there isn't a good way to predict what they're going to do without relying on meta knowledge or implementing your own calls out to C. That's not great for functions we're blessing as part of our standard library.

Now, that's not to say that we couldn't still provide them in the interim. We could choose to mark them as unstable for Chapel 2.0 - they'd still be available to programs, but we wouldn't make any assurances about whether we'd break them in the future. But saying that we might break them in the future isn't great either. So it sounds like you'd push for nailing down rounding support for 2.0, is that fair?

The name has not been around long enough for me to object to a change of name over existing practice.

I have used a method on a real(w) called rintNo754X() but that name might be too spartan for some.

What about something like roundSpecified? roundCurrent? controlledRound? Those all seem a bit long to me, but maybe they'll inspire a better name.

We could even merge rint and nearbyint into a single function that takes a boolean on whether to use the exception version or not, if we were feeling particularly creative.

damianmoz commented 2 years ago

With regard to rint and nearbyint, it was mentioned

we don't really support them much to begin with

If Chapel is going to be IEEE 754 compliant, then this needs to be fixed rather than dropping rint. Unless you are going for backwards compatability, IEEE754-2019 says you can ditch 'nearbyintbut need to implement an exceptionlessRounding-To-Nearest-Ties-To-Even` routine.

Note that any Chapel program must start life with its rounding mode as rounding-to-nearest-ties-to-even. It is not just something I personally know, it is mandated. If I cannot change it, the default will hold. And if I call routines written others which mess with the rounding mode, I assume they are polite enough to restore it to what it was before they did their messing.

If Chapel is going to say it is not IEEE 754 compliant, then that is a whole new ball game.

The best way to attack this problem is to

remembering that a lot of people adopting Chapel might want to use familiar names, that programs need to read well as a description of the mathematics, and be readable by people who want them as an ultimate reference for the algorithm.

I like the fact that you have choices in names but new names are premature at this stage. Also, the fact that there is a routine called round which implements only a single rounding mode, and not the default one at that, makes life hard to start with.

Like roundCurrent, roundSpecified (definitely too long), controlledRound (no thanks), but rint is pervasive. Users love it, people remember it, it rolls off the tongue easily, it reads clearly, and now that nobody needs to remember its mate nearbyint, you would get flak cutting it. My 2c.

A lot of these modes on be implemented in a single machine instruction so merged routines might not cut it, although optimization might prove me wrong. I think ARM has individual instructions for each of the five modes. Intel does not but goes down the backward compatability route. Not sure about RISC-V and others.

Later....

damianmoz commented 2 years ago

The rounding of 'x' is a mapping of x to either a) itself if its fractional part is zero b) one its two non-fractional neighbours

For case (b), the choice of neighbour is driven by the rounding mode, one of:

1) Directed Rounding { Towards Positive | Away from Negative | Above | Up } 2) Directed Rounding { Towards Negative | Away from Positive | Below | Down } 3) Directed Rounding { Towards Zero | Away from Infinity } 4) Rounding To Nearest returning in the event of a tie the ... non-fractional neighbour of x with the larger magnitude (away from zero) 5) Rounding To Nearest returning in the event of a tie the ... non-fractional neighbour of x which is an even integer

There is the concept of the current rounding mode which is one of (1)..(5), which defaults to (5). When that rounding mode is requested at run-time, the CPU chooses the appropriate one. This is called

6) The Current Rounding Mode.

The C/C++ current practice names are:

- ceil
- floor
- trunc
- round
- roundeven
- rint

Chapel supports all except roundeven. All except roundeven are historic names with long provenance. The name 'trunc' goes back to the early 1970s and Pascal.

Unfortunately, with 20/20 hindsight, 'round' was a poor choice because it is only one of the five IEEE types of rounding, and one of two types of round-to-nearest, i.e. where ties are forced away from zero. If its was chosen with the same thought pattern as 'roundeven', it may be called:

roundaway

or roundlarger.

That particular background, and some from Fortran, means some people use other names.

Calling it Enhanced Current Practice A, these would be:

- ceiling - breaking changes(taken from Fortran)
- floor
- trunc
- roundaway - breaking change (but reflects a commonality with 'roundeven')
- roundeven
- rint

That said, 'even', 'away' and 'larger' all have nothing to do with the actual rounding but everything about what happens with a tie, or when the fractional part of the number to be rounded is 0.5. So those names could be a lot more descriptive and be

roundTiesToEven

and roundTiesToAway (or roundTiesToLarger).

The names in Enhanced Current Practice A are a bit messy. A mix of nouns, abbreviations and phrases.

Limiting oneselves to verb[+qualification], we might get a new nomenclature mix.

This results in Enhanced Current Practice II - all breaking but all adjectival (some qualified)

- roundedabove - a.k.a. ceiling
- roundedbelow - a.k.a. floor
- truncated
- roundedTiesToAway or roundedTiesToLarger
- roundedTiesToEven
- rounded - a.k.a. rint

The last name could be

roundedcurrent

but it is long, like so many of the above. Long names look awful in formulae.

Alternatively you can run with nouns[+qualifications] for a different mix:

- ceiling
- floor
- truncated
- nearestTiesToLarger (or nearestTiesToAway)
- nearestTiesToEven
- rounded

Except for the first two, there is not a lot of sound mathematical basis for the names.

And the historical names of the last four are too long, are all less than optimal, and above all breaking unless the old names are retained as aliases for a while before being deprecated.

Maybe you can drop the 'To' in those long names.

As extra background, ....

floor'ed rounding, truncated rounding, and nearestTiesToEven rounding are the basis behind respectively.

remaindering as implemented by Chapel's 'mod' routine
remaindering as implemented by Chapel's '%' operator
remaindering as mandated by the IEE754's 'rem' functionality

Ada & Lisp agree with Chapel and call remaindering by flooring 'mod'.

Fortran & C both call remaindering by truncation 'mod' - Ada calls it 'rem'.

All food for thought.

damianmoz commented 2 years ago

Another simpler approach is a mix of provenance and mechanical name derivation.

Let's grab the names of rounding operations mandated by the IEEE 754 standard:

* Round To Integral Towards Positive
* Round To Integral Towards Negative
* Round To Integral Towards Zero
* Round To Integral Ties To Away
* Round To Integral Ties To Even
* Round To Integral Exact

These are verbatim and are precisely (and clearly) described. Spaces have been inserted into the names to make them more readable to a human.

For the first 3, use the mathematically accepted verb for that operation, i.e.

    ceil    as in, ... to ceil the interior of the roof with plaster or panelling
    floor   as in, ... to floor the accelerator
    truncate

We cannot use truncate because of a name clash with a file operation so let's revert to the abbreviation Wirth used in Pascal 50 years ago, i.e.

    trunc

At least I am in good company if that is a bad choice! Feed free to disagree!

Not unsurprisingly, these match what Chapel (and C/C++) already does, i.e.

    ceil
    floor
    trunc

So no breaking changes so far.

There are no accepted mathematical names for the last 3 operations and using names out of air like tom, dick and harry is not really an option.

Let's take a mechanical approach to this. Looking at the words within the IEEE 754 operation, drop every word except the first and last of that same operation. This is the approach the CFP interest group took when creating a name for the second last case in C23. So we are in good company!!

So dropping all but the first & last words of the operation, we have:

    roundaway
    roundeven
    roundexact

The first is new, the second agrees with C23, the last is arguably better than rint even though that name is well known and easy to remember. Feel free to disagree!!

The name round will be kept as an alias for roundaway in Chapel for the foreseeable future but marked for deprecation at some point. Probably should do the same for rint.

So again, we are not making any breaking changes with these last 3 names.

In summary, the rounding operations then become

ceil
floor
trunc
roundaway
roundeven
roundexact

Not perfect, but this is more functionally complete that what we have and the nomenclature is no worse.

lydia-duncan commented 1 year ago

These are unstable for 2.0, we intend to design more thorough rounding support after that. Removing the 2.0 tag