(e-mail address removed) said:
Because you're engaged in a campaign of personal destruction as you
were with Schildt.
The important thing is that C students learn how to write C properly. The
characteristic you share with Schildt is that you are endeavouring to teach
C without actually knowing it, and /that/ is what is destructive.
No, the mark of a bad programmer is inability to think outside the
particular set of chosen inputs
That's another mark, it's true, but my point is still perfectly valid.
and your insistence that C is still usable for new development.
Well, that's certainly an opinion, although I doubt whether it will be
shared by many here in comp.lang.c.
OK, this is complete nonsense. Are you pulling my leg?
No, I'm perfectly serious, and no, it's not even incomplete nonsense, let
alone complete nonsense. What I have said is accurate.
You read in the standard that "C offers no guarantee with a code
point..."
Not those exact words, but yes, it's true that the Standard offers no such
guarantee.
But it MEANS that there CAN be. Can you read?
Yes, I can read, and yes, there can be characters with a higher code point.
And to use EBCDIC, a completely obsolete encoding, obsoleted by ASCII
and then international 16 bit encoding...the mind boggles!
Some programmers don't get to choose their character set.
In ASCII, for which nearly all C code is written, the letters follow
the numbers! If the user enters A your "correct code" breaks?
Not at all. But to rely on the letters following the numbers (which, I
hasten to add, your code does not) would be silly.
[The "standard" may require the C runtime to treat the digits as
following the characters.
It doesn't. The guarantees given by the Standard w.r.t. code points are as
follows:
1) all characters in the basic source and execution character sets have
positive values;
2) '0' to '9' are in numerical order and contiguous.
The fact is that real compilers don't, and
use the encoded value. What this illustrates is the UNUSABILITY of the
standard, and consequently that it is extremely unwise, to the level of
professional malpractice, to recommend C for new development, as you
appear to.]
No, it illustrates that you don't understand the Standard's guarantees about
code points (see above).
Are you for real? And you are sitting on your fat ass telling me I
don't know how to program?
Well, I don't own a donkey, fat or otherwise, but apart from that you're
broadly correct, yes.
But then you say that there are "just 6 characters" as if the small
number of characters makes it probable that your code won't break!
No, I'm saying that to rely on character ordering that is not guaranteed by
the Standard renders code non-portable to systems where that reliance is
misplaced. I will again say, however, that your code does *not* fall foul
of this particular issue. We are discussing a minor point raised by Andrew
about a curious way of phrasing a character test, not an error in your
code.
"My program is standard C, therefore the user has no right to send
characters with t a value higher than 9" is the zenith of arrogance.
But nobody is saying that.
And you use this as a reason for presenting code which BREAKS if there
IS a code point after all (c - 0 with no test for greater than 9)????
Not at all.
This is worse than I thought.
You seem to be using C on an IBM mainframe with the SAS compiler, and
pompously talking about C based on a misreading of the standard!
Please cite and explain the relevant section of the Standard that you think
I am misreading.
And if your compiler runs on an ASCII system, it has to jiggle
characters at runtime to make sure 9 is the "last" character.
No, not at all. You seem to have misunderstood my point completely.
And C is an "efficient" language?
No, C is a programming language. Whether C programs are efficient depends on
how they are written. It is easily possible to write inefficient programs
in C, just as it is easily possible to write inefficient programs in any
sufficiently powerful programming language. (The caveat is only there
because I suppose it is possible to conceive of a programming language so
limited that all its expressible programs are efficient, but that would
almost certainly be a very limited language indeed.)
Without being even aware that given aliasing, C can't be standardized
in a modern sense?
Well, I'll let you take that up with ISO.
And icing on the cake..."it was also sufficiently grammatical THAN I
could understand it".
Do respond. I need a laugh and it seems you are ... well, not awake,
but posting. Are you drunk? Are you stoned?
No, but I am in the mood to write a Sieve. See below.
First, though, I'll recompile your program to allow writeable strings, and
see what it tells me:
me@here:~/scratch> ./foo 1 10 10
Sieve of Eratosthenes primes from least prime >= 3 to greatest prime <= 10
Sieve contains 10 entries
3
Sieve contains 134520391 entries
5
Sieve contains 134520391 entries
7
Sieve contains 134520391 entries
Number of primes found: 3
Number of nonprimes found using the sieve: 1
Number of nonprimes found using brute force: 0
Number of primes in the sieve: 3
Number of primes displayed: 3
Time in approximate seconds: 0
Curious how the sieve expands suddenly, isn't it?
Incidentally, changing the sieve size to 100000, I get a runtime (as
measured by time(1)) of 5.8 seconds with output redirected to /dev/null (to
eliminate console updating, which is slow). With a sieve size of 1000000,
the runtime is positively chelonian, and I canned it after about 7 minutes.
Here are some comparative performance statistics:
Max Execution time (seconds, excluding display)
prime
value My program Your program
10 0.003 0.002
100 0.003 0.002
1000 0.003 0.003
10000 0.004 0.081
100000 0.014 5.808
200000 0.017 20.102
300000 0.024 46.097
400000 0.060 too long
And here's my program, which, in the interests of fairness, I have made 200
lines long:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <time.h>
#include <math.h>
#define DEFAULT_SIEVE_BYTES 1024UL
#define DEFAULT_SIEVE_SIZE (DEFAULT_SIEVE_BYTES * CHAR_BIT)
/* Bitmapping macros */
#define BYTE(array, bit) ((array)[(bit) / CHAR_BIT])
#define MASK(bit) (1 << ((bit) % CHAR_BIT))
#define SET_BIT(array, bit) \
BYTE(array, bit) |= MASK(bit)
#define CLEAR_BIT(array, bit) \
BYTE(array, bit) &= ~MASK(bit)
#define TEST_BIT(array, bit) \
(!!(BYTE(array, bit) & MASK(bit)))
/* Helper function prototypes */
void show_help(void);
void do_sieve(unsigned long first, unsigned long last);
void sort_two_uls(unsigned long *a, unsigned long *b);
/* program entry point */
int main(int argc, char **argv)
{
int rc = EXIT_SUCCESS;
/* start the clock */
time_t start = time(NULL);
/* capture and crop limits */
if(argc > 2)
{
char *end1 = NULL;
char *end2 = NULL;
unsigned long first = strtoul(argv[1], &end1, 10);
unsigned long last = strtoul(argv[2], &end2, 10);
sort_two_uls(&first, &last);
if(end1 == argv[1] || end2 == argv[2])
{
show_help();
fprintf(stderr,
"Invalid argument %s\n",
end1 == argv[1] ? end1 : end2);
rc = EXIT_FAILURE;
}
else if(last < 2)
{
printf("Trivial case - no primes in list.\n");
rc = EXIT_FAILURE;
}
else
{
/********************
* All is well. *
* Go do the sieve! *
********************/
do_sieve(first, last);
}
}
else
{
show_help();
rc = EXIT_FAILURE;
}
if(rc == EXIT_SUCCESS)
{
/* Stop the clock and display the runtime */
time_t end = time(NULL);
fprintf(stderr,
"Approximate execution time (seconds): %.0f\n",
difftime(end,
start));
}
return 0;
}
void do_sieve(unsigned long first, unsigned long last)
{
unsigned long candidate = 0;
unsigned long root = 0;
unsigned long scratch = 0;
unsigned long primes = 0;
unsigned char default_sieve[DEFAULT_SIEVE_BYTES] = {0};
unsigned char *sieve = default_sieve;
if(last > DEFAULT_SIEVE_SIZE)
{
size_t bytes = last / CHAR_BIT + 1;
sieve = calloc(bytes, sizeof *sieve);
if(sieve == NULL)
{
fprintf(stderr,
"I can't build a sieve that big.\n"
"I'm using the default sieve size\n"
"(which is %lu) instead. Sorry.\n",
DEFAULT_SIEVE_SIZE);
sieve = default_sieve;
last = DEFAULT_SIEVE_SIZE;
if(first > last)
{
first = last / 2;
fprintf(stderr,
"That means I have to adjust\n"
"the range as well. I am now\n"
"using the range %lu - %lu\n",
first, last);
}
}
}
/* OKAY! We can now use our sieve. At present, it
* is populated entirely with zero-bits. We will
* take 0 to mean "might be prime", and 1 to mean
* "is definitely not prime". At the end of the
* process, of course, 0 will mean "IS prime".
*/
/* find the root of the upper limit */
root = sqrt(last) + 1;
/* Mark 0 and 1 as being non-prime */
SET_BIT(sieve, 0);
SET_BIT(sieve, 1);
/* Mark 4 as non-prime */
SET_BIT(sieve, 4);
/* Mark all numbers of the form 6n, 6n + 2, 6n + 3
* and 6n + 4 as being non-prime.
*/
for(candidate = 6; candidate <= last; candidate += 6)
{
SET_BIT(sieve, candidate);
SET_BIT(sieve, candidate + 2);
SET_BIT(sieve, candidate + 3);
SET_BIT(sieve, candidate + 4);
}
for(candidate = 5; candidate <= root; candidate += 2)
{
for(scratch = candidate * 2;
scratch <= last;
scratch += candidate)
{
SET_BIT(sieve, scratch);
}
}
for(candidate = first; candidate <= last; candidate++)
{
if(!TEST_BIT(sieve, candidate))
{
printf("Prime found: %lu\n", candidate);
++primes;
}
}
fprintf(stderr, "Total primes found: %lu\n", primes);
if(sieve != default_sieve)
{
free(sieve);
/* help to protect against maintenance errors */
sieve = NULL;
}
return;
}
void show_help(void)
{
const char *help[] =
{
"\n This program uses Eratosthenes' Sieve to work\n",
" out all the primes between two limits. Please\n",
" run the program again, specifying both limits.\n\n",
NULL
};
int i = 0;
do
{
fputs(help[i++], stderr);
} while(help
!= NULL);
return;
}
void sort_two_uls(unsigned long *a, unsigned long *b)
{
if(*a > *b)
{
unsigned long t = *a;
*a = *b;
*b = t;
}
}