Re: Preprocessor limitation workarounds



Eric Sosman wrote:
D Yuniskis wrote:
Eric Sosman wrote:
D Yuniskis wrote:
[...]
I think you missed one of the points:

#define FOO (10000000000/10000000000)

is an integer constant expression that *evaluates* to something
not exceeding ULONG_MAX (i.e., "1"). But, the preprocessor
can't *evaluate* it (because all of its component expressions
must also satisfy this criteria.

No, I didn't miss the limitation. I stated the assumption
that FOO was "an integer constant expression," but your example
is a C90 I.C.E. only if ULONG_MAX is at least 10000000000. (In
C99, UINTMAX_MAX is guaranteed to exceed 10000000000, so you're
home.)

Exactly. But ULLONGs exceed this!

ULLONG_MAX cannot exceed UINTMAX_MAX, by definition.

I don't see UINTMAX_MAX in C89 (see my original post)

Here's what my BigDecimal headers had to do (PSEUDO-code...
off the top of my head as it has been a while since I
tinkered with this):

#ifndef RADIX
# define RADIX (10)
#endif

#if (RADIX % 10) != 0
# error "RADIX must be an integral power of 10."
#endif

Since you've already chosen to waste some space by working
in powers of ten instead of powers of two, I'm not sure why
"most tightly" (from one of your messages elsethread) is so
important to you.

I work in powers of 10 (in this library) because it
allows "exact" representations of decimal values.

"Most tightly" because it would be foolish to use
"ints" with RADIX=10 as even 16 bit ints would result in
half the memory being "empty".

// pick a type to hold a single RADIX "digit"
#if (RADIX - 1) < UCHAR_MAX
typedef unsigned char digit_t;
#elif (RADIX - 1) < USHRT_MAX
typedef unsigned short int digit_t;
#elif (RADIX - 1) < ULONG_MAX
typedef unsigned long int digit_t;
#elif (RADIX - 1) < ULLONG_MAX
typedef unsigned long long int digit_t;
#else
# error "Unable to represent RADIX using standard data types"
#endif

I'm not sure why you use < instead of <= (I mention it
because you've done the same elsethread, and I'm beginning
to think it might not be just a typo).

It's a (consistent) typo on my part. :> It changes things
by "1" (hardly significant to the problem being discussed)

Also, the #error message is at best misleading, since
your #if ladder does not exhaust all the "standard" integer
types.

As I mentioned, I'm writing this off the top of my head
merely to illustratet to readers the *type* of thing I
am trying to do. I would assume someone could grasp
the *intent* of the #error without worrying about my
choice of words, here (I suspect there are *spelling*
errors in my posts as well! :> )

// pick a data type to hold a digit sum
#if (RADIX - 1) + (RADIX - 1) + 1 < UCHAR_MAX
typedef unsigned char sum_t;
#elif ...
...
#else
# error "Unable to represent the sum of two digits"
#endif

Do some simple algebra:

#if RADIX - 1 < /*sic*/ UCHAR_MAX - RADIX

Yes, but it still doesn't *solve* (all of) the problem(s).
And, it obfuscates what the conditional is testing for
(especially important when trying to give an example in
a forum where folks don't know *why* one would want to
do something "crazy" like this...)

// pick a data type to represent a digit product
#if ((RADIX - 1) * (RADIX - 1)) + (RADIX - 2) < UCHAR_MAX
typedef unsigned char product_t;
#elif ...
...
#else
# error "Unable to represent the product of two digits"
#endif

Algebra again.

// other similar invariants imposed on the data types ...

Choice of RADIX characterizes the algorithm in space and time.
For a "typical" compiler, I would opt for a RADIX of "10"
for speed and "99999999" for space (efficiency).

The latter choice would make one of your #error directives
fire.

Why? ULLONG_MAX will handle the most demanding of the
conditions (i.e., product-t).

Also, the belief that "smaller RADIX means greater speed"

It's not "a belief". It's the result of *measured*
design tradeoffs. Again, my original post stipulates
that this is used in a variety of targets (with varying
processor characteristics as well as application needs)

needs more thought. It is possible that individual arithmetic
operations might be faster in smaller types than in wider types,
but for a given range of numbers you will need more digits and
thus perform more operations. A given value V will have roughly
eight times as many radix-10 digits as radix-100000000 digits.

But, you're assuming all V's are equally likely.
If, for example, you are dealing with small-ish V's
the overhead of pulling wide values from memory,
manipulating them and then re-storing them dominates
the process.

As you can see, the conditions for the "space" choice can't
be handled by the preprocessor even if the compiler supports
long longs!

The preprocessor *is* powerful enough for at least some of
the calculations you've shown. But no, it's not all-powerful.
That's why several people have suggested you write helper programs
in a more powerful language -- C, for example -- to do what the
preprocessor cannot do, or can do only with difficulty.

And, you will note, that I have explained that that is the
approach I *have* taken.

You will also note the reasons I have explained for not liking
this approach.

And the motivation for seeing if others facing similar issues
have taken any *other* approaches to solve the problem.

But if you won't take advice ... Well, one wonders why you
bother asking for it. Good-bye.

Don't forget your ball and bat...
.



Relevant Pages

  • Re: Preprocessor limitation workarounds
    ... not exceeding ULONG_MAX. ... #ifndef RADIX ... # error "Unable to represent the product of two digits" ...
    (comp.lang.c)
  • Re: Tip du jour: no need to chomp before to_i
    ... #to_i just grabs the first sequence of digits, ... number and turns it into an int. ... otherwise it will turn the run of digits at the front of the ... string into an integer according to the radix. ...
    (comp.lang.ruby)
  • Re: Tip du jour: no need to chomp before to_i
    ... #to_i just grabs the first sequence of digits, ... number and turns it into an int. ... to_i is passed the radix, ... It will return zero if the first character is not the set of digits for ...
    (comp.lang.ruby)
  • [announce] Performance Counters for Linux, v6
    ... load pointer to pda into %gs while brining up a CPU ... The type of **_id is int. ... +The Linux Performance Counter subsystem provides an abstraction of these ...
    (Linux-Kernel)
  • Re: Linux 2.6.27.8
    ... struct pt_regs *regs); ... static int printbinary ... void show_regs ...
    (Linux-Kernel)