Here is some iteratively developed discussion logic that may help guide choices. It has seen some feedback by other users of floating point number crunching software, albeit a small sample.
What are we trying to do here? We want to provide the functionality for the General Operations of 5.3.1 of the 2019 IEEE 754 standard:
Let us eliminate the last for now. C/C++ implements that rint. Leave it at that. It is a very different beast anyway as it relies on the currently active rounding mode.
like most standards, correct language is not top of the priority list. In our programs, readability (which includes correct language) is top. I use Oxfords Thesaurus, The Economists style guide, Fowlers Modern English usage to help my choice of nomenclature. I then use the Yale Professor Strunks "The Elements of Style" as the definitive reference so my American English is tolerable. I check with Merriam-Webster when I am really desperate.
The first thing we notice is that the IEEE 754 names are unclear. The first two are rounding to the Nearest non-fractional floating point number between which a (potentially) fractional number will fall. The standard calls these non-fractional numbers an Integral floating point number, quite a different thing to what Chapel says is an integral number. In the event of a tie, there is a choice of two. The last three are directional roundings and there is no choice in which adjoining integral is chosen. The direction is mandated. So it is not the nearest integral we want in this case but the next integral value in some specified direction. The word
next sounds too much like a keyword and is too close to Nearest, so a 7-letter word meaning next or subsequent or following is
Ensuing. It is not too important as that name will diseppaer later. I also fixed the Americanism in the word Toward in the above.
So we want an enum type that can represent:
These are way too long for an identifier, especially when it will appear in some sort of mathematical expression as that are pretty well unreadable.
Use the first 7 letters as the enum name to give us global scope and avoid namespace clashes, and then drop the word
Integral because rounding always implies Integral. So we now have, if we want a single enum to represent rounding modes:
Each of these, complete with the enum name are effectively an identifier. Still too long. So, what can we strip without compromising the meaning?? To get an idea of how they might appear in practice, we will exploit these in the context of the routine frem as well as with the routine round(). Why? Because such usage proves whether what we have is truly readable when we use rounding within an operation. So we have the rounding mode used in a remainder operation as an argument to the routine frem, as in
r = frem(x, y, some IEEE 754 rounding mode);
Let us drop "EnsuingTo" from the last 3 and drop the capital from Nearest.
roundTo.nearestTiesToEven, // 7+17 = 24 characters long
roundTo.nearestTiesToAway, // 7+17 = 24 characters long
roundTo.wardsPositive, // 7+13 = 20 characters long
roundTo.wardsNegative, // 7+13 = 20 characters long
roundTo.wardsZero, // 10+9 = 27 characters long
(r, q) = frem(x, y, roundTo.nearestTiesToEven);
(r, q) = frem(x, y, roundTo.nearestTiesToAway);
(r, q) = frem(x, y, roundTo.wardsPositive);
(r, q) = frem(x, y, roundTo.wardsNegative);
(r, q) = frem(x, y, roundTo.wardsZero);
Even in the above simple assignments, we have 40 characters which is ugly and affecting readability. Those 24 character identifiers are basically unreadable. In an expression, it will be worse. We find a 16(20) characters is (almost) tolerable in terms of readability for an identifier to be used in a long mathematical expression.
So we definitely need a qualifier to handle the case of a tie, i.e when the fraction part is 0.5. Even stripping the
Ties is insufficient. So, the top two must be split into two.
Handling the second pair which are right on the limit, we seek solace in using the number line used in floating point arithmetic. So, the ensuing integer in the Positive direction beyond a floating point number is just the integer ABOVE it, and that in the Negative direction is just that BELOW it. So
roundTo.nearest + needs a qualifier
roundTo.nearest + needs a qualifier
You could use up and down instead of above and below but there is too much risk that somebody will want to use the former pair for keywords. So we do not want to buy ourselves future namespace clash grief. Sorry, I left those up and down bombs in the earlier document hoping somebody would challenge their choice.
// !!! Aside
I would like to use rem by itself, but that might be too greedy if someone wants that keyword as an operator at some future stage. We could also use the long word remainder, although it may be too unwieldy for a function name which will often appear in long expressions.
q in the above is related to the quotient, it is likely not the quotient, that being too hard to compute in a remainder operation of two floating point numbers in a general sense.
// !!! end Aside.
Let us address rounding towards zero by making it go away. Overload
% for a remainder of two floating point numbers
y using rounding towards Zero (which is just truncation as done in the C routine fmod for floating point numbers and as implemented in C/C++ sand Chapel for integers with the
% operator) and we have
r = x % y; // compute the remainder of x and y using truncated division
and in the interim, while we are implementing that (new) operation, just use
r = fmod(x, y);
So we are left with identifiers for the rounding modes which are not overly long
roundTo.nearest + qualifier
roundTo.nearest + qualifier
Not perfect because
nearest is an adjective while
below are adverbs.
The qualifiers for use with the first two, along with the non IEEE 754 strategy of using the
odd integral value nearest the tie, will be
Lean and Mean. Let's try is
(r, q) = frem(x, y); // IEEE 754 Standard 5.3.1
(r, q) = frem(x, y, roundTiesToAway);
(r, q) = frem(x, y, mode = roundTo.above);
(r, q) = frem(x, y, mode = roundTo.below); // equivalent to Chapel's mod(x, y)
Note overly long-winded.
Now, does this work with the round routine, where round is a verb?
r = round(x); // to nearest, ties to even is the default
r = round(x, roundTiesTo.away); // to nearest, ties to away
r = round(x, roundTiesTo.odd); // to nearest, ties to odd
r = round(x, mode = roundTo.above); // round Towards Positive
r = round(x, mode = roundTo.below); // round Towards Negative
Seems to. The latter two should have the aliases of
ceil and floor`, which again are both English language verbs.
What happened to our rounding Towards Zero, or truncation. While IEEE 754 specifies truncation as a kind of rounding, some mathematicians disagree. Let us not complicate things and keep it separate, i.e retain the status quo
r = trunc(x);
However, I have hated trunc ever since I saw it in Pascal many years ago. We could find a better English verb, say 5-letters like round, like say prune for this task, or do we just use the full word truncate and avoid the abbreviation.
r = prune(x);
r = truncate(x);
The former is also a softer sounding word in English like round. We could even use the word chop but this is a little bit loose, and again it has a harsh sound and reads poorly. Hmmm, maybe prune might be too radical!!!!
Finally, should the enumtype be called
roundingMode because that is correct English. Or, because it is done as an optional argument, should it be
roundModeIs. Or something else????