I want toi have these as a param which obviously means they cannot be a *range. I also notice that the upper end of the range differs from the lower end by only 2 or 3.
Which of the following might be considered de rigeur for the case of real(32) say:
proc packedRangeA(type T) param where T == real(32) do return ((6 << 8) | 9):uint(32);
proc packedRangeB(type T) param where T == real(32) do return ((3 << 8) | 9):uint(32);
I do not see the advantage of A over B or visa versa, based on what you wrote. Other than, obviously, "B" means you need an extra operation to calculate lo.
The most elegant solution is to define three separate param functions to calculate range low, range high, and range length, given the type argument.
However, this depends on what your objective is. The "three separate functions" approach fails when you want to calculate your range once and pass it around as a param, so that the recipient can use it without knowing what type it is for.
Another approach could be to create a record with param fields for low and hi -- or with param methods for low and hi. Then you can pass this record around and get param low and hi out of it.
record paramRangeA {
param lo, hi;
proc len param do return hi - lo + 1;
proc init(type T) {
lo = (compute based on type);
hi = (compute based on type);
}
}
consume(new paramRangeA(real(32));
record paramRangeB {
type T;
proc lo param { compute based on type; }
proc hi param { compute based on type; }
proc len param do return hi - lo + 1;
}
consume(new paramRangeB(real(32));
To much work for your team. There are other far more pressing problems that they can attack. Certainly not high on my wish list. Something to attack in 2030!
I agree with you that a separate functions approach is the most elegent idea.
Hoiwever, finding names for those functions is a nightmare. Specifically the two numbers, the lower and upper found, are what Kahan called "Significant Decimals" or "Sig. Dec." but that name nor the abbreviation never took off. If you need the original reference 1994(?) paper, I can provide it. Sadly, the name by which those two individual numbers are known in the context of the C and C++ standards are not the most intuitive (but then I have the benefit of 30 years of hindsight and usage so my complaint is in very bad taste). In the Fortran standard, it is actually a broken name in my humble opinion, a flaw which has now spread to NUMPY. See my comment on #14016 on Github. For now I just use something like _packedRangeA and call the range the extent of some real(w) type:
// Paraphrasing Kahan's discussion about "significant decimals", or
// more precisely significant decimal digits, which he abbreviated
// as "sig. dec.", then a integral range M..N (or what is now called
// "extent") exists for any W-bit floating point number M < N < W/2
// such that (in the absence of Overflow or Underflow):
// If a decimal text string with at most M sig. dec. is converted to
// such a number and then converted back to a string with the same
// number of sig. dec., that final string should match the original.
// If such a number is converted to a decimal text string with at
// least N sig. dec. and then converted back to such a number, that
// final number must match the original.
proc extent(type T) param where isReal(T)
{
param w = numBits(T);
param N = (w >> 2) + (if w == 128 then 4 else 1);
param M = N - (if w == 32 || w == 128 then 3 else 2);
return ((M << 8) | N):uint(w);
}
proc extent(x : real(?w)) param do return extent(real(w));
I am not convinced that the name extent() is perfect but it works for me and a small group of others. It needs a lot more thought and evaluation of its use in practice if I were proposing its use as a standard...., expanse, bandwidth, immensity, capacity, , ...
Both M and N are related to the accurate visual representation of numbers in decimal digits, decimal digits being how moist of us represent floating point numbers on a day to day basis.
The names -- any names -- are relative to the context. I hope these work in your context!
For a more "production" use of your extent function, I suggest adding a comment that explains what it returns and/or how to obtain M and N from its return value. Otherwise I have to look at its implementation to figure it out.
M : the number of base-10 digits that can be rounded into a type T and back without change
N : the number of base-10 digits needed to uniquely represent all distinct values of the type T
I have never been able to create a lean and mean, practical and memorable, name from either of those descriptions of reasonable size. The best I have which are still questionable, i.e. far too verbose:
max Decimal Digits That Survive Copy Into Read From A(T).........Text->Real-Text
min Decimal Digits Needed To Uniquely Represent Any(T) .........Real->Text->Real
I have more comments in my code. I was trying to be economical in my post.
I too would be extremely impressed when somebody creates short phrases that explain these clearly. Even Kahan grouped them together and called them Significand Decimals with an abbreviation of Sig. Dec.
Your suggestion for the comment is not so easy, because the return type is uint(32) for a real(32) argument and is uint(64) for a real(64). How about I change the name of the routine to resolution and add 2 lines to the comments like:
// Paraphrasing Kahan's discussion about "significant decimals", or
// more precisely significant decimal digits, which he abbreviated
// as "sig. dec.", then a integral range M..N (the "resolution")
// exists for any W-bit floating point number M < N < W/2 such that
// (in the absence of Overflow or Underflow):
// If a decimal text string with at most M sig. dec. is converted to
// such a number and then converted back to a string with the same
// number of sig. dec., that final string should match the original.
// If such a number is converted to a decimal text string with at
// least N sig. dec. and then converted back to such a number, that
// final number must match the original.
// given some R = resolution(real(w)), M = R >> 8 and N = R & 0xff,
// both M and N are returned in the two least significant bytes
proc resolution(type T) param where isReal(T)
{
param w = numBits(T);
param N = (w >> 2) + (if w == 128 then 4 else 1);
param M = N - (if w == 32 || w == 128 then 3 else 2);
return ((M << 8) | N):uint(w);
}
proc resolution(x : real(?w)) param do return resolution(real(w));