Reputation: 28873
I am writing an integer-like class that represents a value that lies somewhere in a range. For instance, the value of bounded::integer<0, 10>
is somewhere in the range [0, 10]. For this class, I have defined radix
to be 2
.
What should the value of digits
be for bounded::integer<-100, 5>
?
What about bounded::integer<16, 19>
?
Upvotes: 7
Views: 2460
Reputation: 28873
After reading the standard a bit more and thinking about this, I believe I have the best answer, but I am not certain.
First, the definition of digits
, taken from the latest C++14 draft standard, N3797, § 18.3.2.4:
static constexpr int digits;
8 Number of
radix
digits that can be represented without change.9 For integer types, the number of non-sign bits in the representation.
10 For floating point types, the number of
radix
digits in the mantissa
The case of bounded::integer<-100, 5>
is the same as for bounded::integer<0, 5>
, which would give a value of 2
.
For the case of bounded::integer<16, 19>
, digits
should be defined as 0
. Such a class cannot even represent a 1-bit number (since 0
and 1
aren't in the range), and according to 18.3.2.7.1:
All members shall be provided for all specializations. However, many values are only required to be meaningful under certain conditions (for example,
epsilon()
is only meaningful ifis_integer
isfalse
). Any value that is not "meaningful" shall be set to 0 or false.
I believe that any integer-like class which does not have 0
as a possible value cannot meaningfully compute digits
and digits10
.
Another possible answer is to use an information theoretic definition of digits. However, this is not consistent with the values for the built-in integers. The description explicitly leaves out sign bits, but those would still be considered a single bit of information, so I feel that rules out this interpretation. It seems that this exclusion of the sign bit also means that I have to take the smaller in magnitude of the negative end and the positive end of the range for the first number, which is why I believe that the first question is equivalent to bounded::integer<0, 5>
. This is because you are only guaranteed 2 bits can be stored without loss of data. You can potentially store up to 6 bits as long as your number is negative, but in general, you only get 2.
bounded::integer<16, 19>
is much trickier, but I believe the interpretation of "not meaningful" makes more sense than shifting the value over and giving the same answer as if it were bounded::integer<0, 3>
, which would be 2
.
I believe this interpretation follows from the standard, is consistent with other integer types, and is the least likely to confuse the users of such a class.
To answer the question of the use cases of digits
, a commenter mentioned radix sort. A base-2 radix sort might expect to use the value in digits
to sort a number. This would be fine if you set digits
to 0
, as that indicates an error condition for attempting to use such a radix sort, but can we do better while still being in line with built-in types?
For unsigned integers, radix sort that depends on the value of digits
works just fine. uint8_t
has digits == 8
. However, for signed integers, this wouldn't work: std::numeric_limits<int8_t>::digits == 7
. You would also need to sort on that sign bit, but digits
doesn't give you enough information to do so.
Upvotes: 6
Reputation: 70546
You are over-thinking it. There are two simple options for digits
specializations for your own ranged_integer
log2(Last-First)
when you are representing the range [First, Last)
. N * numeric_limits<U>::digits
which corresponds to the smallest underlying storage std::array<U, N>
in which you can store your range.Note that your class ranged_integer
can internally do a transformation to map a range of e.g. [-100, 5]
onto [0, 105]
so that you don't have to worry about sign-bits etc.
Upvotes: 0