Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

Letters


APR92: LETTERS

C the Light

Dear DDJ,

I read your interview with Tom Pittman ("Programming Paradigms," January 1992) and I'm amazed by the people who don't like C. Tom says, "I saw an interview with one of the designers of C that said C was not intended for large projects. Absolutely right." I think he was referring to the quote by Dennis Ritchie in the September, 1990 issue of BYTE where Ritchie said, "One of the basic criticisms that can be made of the [C] language is that it doesn't help much in doing large projects (i.e., it was not designed with big monolithic programs in mind). It was designed in an environment of multiple small programs that interact only by fairly restrictive means."

I'm not sure he was responding to what the designers meant. Mr. Ritchie told me the following in personal communication:

One might also say that the real problems with large monolithic projects is that they are so monolithic, and we need to find better ways of cutting them into pieces, and that this problem is not closely related to any particular programming language.

Nevertheless, I have to admit that C was not designed with an eye towards writing very large programs; for example, it doesn't encourage careful control over name visibility and modularization. It's fine for writing limited-size tools that interact in an environment like UNIX, using extra-language mechanisms like file I/O. But very large single programs suffer from the problem of "change one #include file, recompile everything" -- which is a real problem if recompilation takes a day.

One of the contributions I think we made was to demonstrate how far the small program/tool approach could go. But it doesn't necessarily cover everything; there really seem to be gigantic programs that people have to write.

I really think we have work to do when we consider what "large" means. Large is in the eye of the individual's scope and talents. Studies have shown the quantity of lines of code differ by a factor of 5 between 12 experienced programmers. [Editor's Note: see Sackman, H., W.J. Erikson, and E.E. Grant, "Exploratory Experimental Studies Comparing Online and offline Programming Performance," CACM, 11, 1 (January, 1968).] This implies the programmer(s) is enormously important when figuring our size.

Mr. Pittman's attitude is, "Why should I use a brain-dead language like C?" I've found C very easy to understand, modify, write, and maintain quality code with. (It is very difficult to do anything with bad C code.) Programmers with outstanding intellects program in this "brain-dead language." C does not impose restraints on the programmer; it lets the programmer do things the computer is capable of.

Marty Leisner

Rochester, New York

Time 2 Review CTime2

Dear DDJ,

I read Kenneth Roach's "Using the Real-Time Clock" (DDJ, June 1991) with interest because I like to peek inside the PC and understand how the hardware works. I often work on time-related applications, and I didn't have all those details about RTC status registers at hand, so this topic was especially important to me. Thank you, Kenneth.

But alas! There is nothing perfect on earth. While reading the listings, I found, to my surprise, that the CTime2 function reads RTC ports, even though it must "emulate C language ctime( ) function." The point is that ctime( ) must only convert supplied values, irrespective of current time. On rereading the code, I found that values taken from ports are not used, except from the Bias variable, which is used for leap-year processing. (I didn't try this code, but it seems to me that it wouldn't always work right, even when used to convert current date/time.)

So I've written my own ctime2( ) and tested it in Microsoft's QuickC 2.01 and C 6.0 as well as Borland Turbo C++ and Borland C++. I calculate time and day of week the same way Mr. Roach did. Then I determine the number of leap years between 1970 and a given date and delete all February 29s. This makes it possible to pretend leap years don't exist (considering February 29 a special case), which dramatically simplifies calculations. Day and month can also be found much more easily than in CTime2 using arrays containing numbers of days from the beginning of the year to the beginning of the each month.

I was thinking about writing Pascal equivalents, but I was too lazy--and besides, I think C code is much more interesting because it shows usage of C time-zone and daylight variables. To my surprise, I found that my function turned out to be much faster than its library prototype, as you can see in Table 1.

Table 1: Yuri's ctime2() versus ctime() test results

  QuickC
  ctime2()  called 20586 times
  ctime()   called 6543 times
  ctime2()  315% faster than ctime()

  Borland C++
  ctime2()  called 7624 times
  ctime()   called 2707 times
  ctime2()  282% faster than ctime

  Turbo C
  ctime2()  called 7660 times
  ctime()   called 6673 times
  ctime2()  345% faster than ctime()

  MSC 6.0
  ctime2()  called 22987 times
  ctime()   called 2706 times
  ctime2()  284% faster than ctime()

I found it curious that in these experiments, Microsoft compilers (even QuickC) generated code that ran faster than Borland's. To try to understand why, I used Borland's Turbo Profiler to determine that lines performing long arithmetic take more than the half of ctime2( ) execution time. (I was profiling the code generated by Borland C++.) When I changed test programs, removing as much long arithmetic as possible, Borland compilers showed significant improvement. Moreover, when debugging my ctime2( ), I found a minor error in the Turbo C++ library ctime( ): It didn't correctly determine the beginning and end of the daylight-savings period. This error was corrected in Borland C++.

Yuri Elik

St. Petersburg, Russia

Editor's note: The source code to Yuri's implementation of CTime2 is available electronically.

Are the Emperor's New Threads Really New?

Dear DDJ,

Judging from your magazine, the object-oriented paradigm is now firmly entrenched in the minds, if not the hearts, of "professional" programmers.

Back in the days of yore, when reusable code was called a library, I accepted the concept and the value of code reusability as being intuitively obvious. Now that the concept has been elevated to the level of an abstract art, I'm not sure anymore. From the humble perspective of a "nonprofessional" programmer struggling to keep pace with the ultramodern programming vernacular, much of the object-oriented paradigm appears to be a polymorphic artifice inherited from the marketing departments of large software corporations that are repackaging their old methods in an attempt to encapsulate the resources of a large class of the computing community. OOPS, did I say something bad?

Ernie Deel

Atlanta, Georgia

Generic Swap Macro

Dear DDJ,

I am a student at the Colorado School of Mines, and just got into C programming about a year ago. I was thumbing through my old issues and came across C language question #36: "How can I write a generic macro to swap two values?"

Throughout my short exposure to C, this has been one of the most frequently asked questions. Unfortunately, the answer has always been something like, "There is no good way to write a generic swap macro." Usually one is told to write a swap macro for a specific data type such as integers and floats, or to forget a macro altogether. I have always felt that with all the tools that C provides, there should be a way to write such a macro. The problem is that swapping requires a temporary variable, and different data types occupy different amounts of memory. After some work, I realized what is needed is an inline variation of the memcpy function. See the macro in Example 1 for my approach.

Example 1: Greg Renzelman's generic swap macro

  #define SWAP (a,b)\
  {\
      unsigned int i;\
      char tmp, *aptr=(char *) & (a), *bptr=(char *) & (b);\
      for (i=0; i<sizeof((a)); ++i, ++aptr, ++bptr) \
   {tmp=*aptr; *aptr=*bptr; *bptr=tmp;} \
  }

With this single macro, I was able to swap integers, floats, pointers, arrays, and structures. The only restriction I see this macro has is that the elements to be swapped occupy the same amount of memory. The two elements to be swapped don't even have to be of the same data type.

I would appreciate hearing any ideas and comments about my idea and implementation of a generic swap macro.

Greg Renzelman

Golden, Colorado

When One Pixel is Better than Two

Dear DDJ,

I needed a good circle-drawing algorithm, and I remembered there had been one in DDJ. Looking in my back issues, I found the article by Tim Paterson, ("Circles and the DDA," July 1990), as well as an improvement by V. Venkataraman ("Letters," July 1991). However, after implementing both versions I had problems. It seems that no effort was made to eliminate the drawing of the same pixel twice. This becomes quite a problem when you are XORing a circle onto the screen. In addition, the pixel plotting can be speeded up significantly.

Looking at the previous implementations of the plot8 function, you can see that it calls the plot function eight times with the x-y coordinates in all eight combinations of +,-, and reversed. Unfortunately, the plot function performs the aspect ratio scaling. As a result, all four combinations of the coordinates (x,y, x,-y, -x,y, and -x,-y) are scaled. This is unnecessary, as only the sign has changed in each different case, not the magnitude. Notice that this is also true for the four combinations of y,x.

This is how I handled aspect scaling in my version of the plot8 function: First, two new variables are needed. This allows us to swap the x and y values efficiently. I chose the obvious: x1, y1, x2, and y2. Since the SetAspect function uses a scaling value of 65,536 to avoid floating-point multiplication, I was able to gain a small speed improvement by casting XAspect and YAspect as (int) when I check to see if scaling is applied.

I eliminated the gaps in round ellipses (circles) by plotting only one pixel in cases where xn or yn is 0, as well as those where x is equal to y. I chose to compare x2 and y2, as the addressing is more efficient in my C compiler (Borland C++).

Now true circles are running quicker, and the gaps have been eliminated, but there are still ellipses to contend with. When drawing an ellipse, some pixels near the "skinny" ends of the ellipse will be drawn twice. Fortunately, these are easy to catch as they result from the scaling, and will occur in sequential fashion. It is only necessary to keep track of the previous "scaled" pixels to ensure there are no gaps. I then added static variables x_last and y_last. It might be better to make these global variables, or place them in the circle function.

At this point, my changes to the circle function are basically complete. However, one more issue needs to be addressed. When drawing real circles to a screen on a 80x86 machine, rather than just testing the speed of algorithms, you have the video adapter to contend with. Since the PC only has a 64K footprint to draw in, the video adapters use various registers to control video "modes." The OUT instruction (used to set the register value) takes up quite a few CPU cycles, especially if the mode registers are SET and RESET for each pixel. Additionally, most video adapters are still "on the bus." This eats up an enormous number of cycles for each OUT to the controller register, and for each pixel drawn. We can't get around this, but we can design our code to use the least number of OUT instructions.

I also changed the circle function by adding a call to setGCregs immediately before the main loop and a call to resetGCregs immediately after. These functions set and reset the Graphics Controller registers on my VGA board. This caused some trickle down into the rest of my code. You can see in plot8 where the XOR command is now missing from my SetPixel function.

Table 2 shows the execution times of my five circle drawing programs. I used a 16-MHz 386SX PC with a Cardinal VGA732a running in 800x600x16 mode for my tests. In reality, only the timings for circle1, circle4, and circle5 should be compared, as the other two are intermediate steps. Notice that in going from circle1 (the benchmark) to circle4, where all gaps are removed and plot8 is improved, we only managed a 1.33 times improvement in speed. It was only when we improved the video adapter register management that we realized the 2.46 times total speed improvement in circle5.

Table 2: Comparison of Robert Stewart's circle-drawing routines

  circle1 57.62 sec  benchmark
  circle2 44.05 sec  1.31xbenchmark
  circle3 44.10 sec  1.31xbenchmark
  circle4 43.00 sec  1.34xbenchmark
  circle5 23.13 sec  2.49xbenchmark

One last thing. The first time I ran circle5, it took over 24 seconds. I was able to reduce it to 23 seconds just by reducing bus memory wait states and bus I/O waits. The timings for the other circle programs were similarly improved, as shown in Table 2.

Robert W. Stewart

El Segundo, California

Editor's note: Robert's plot8 source code and executables are available electronically.

Forth Fans

Dear DDJ,

In Jack Woehr's "Forth: A Status Report" (October, 1991), I read that the 0= vs. NOT issue is still a hot one.

I have always thought it to be counter-intuitive to state "0=IF" instead of "NOT IF" when I want to either do something on a FALSE condition or handle the FALSE condition in an IF...ELSE... construct first. I circumvented the issue by providing the words -IF, -UNTIL, and -WHILE, which can be implemented as : -IF (? --) COMPILE 0= [COMPILE] IF ; IMMEDIATE, or more efficiently, when one can change the nucleus, as: -IF (? --) COMPILE -BRANCH >MARK; IMMEDIATE, where -BRANCH is the "negative" counterpart of ?BRANCH.

In this area there is a second pitfall. When you want to do something, and two values are nonzero, "AND IF" will not do the trick (e.g., "1 2 AND" will give FALSE). One of the logical TRUEs has to be converted to "all bits set" with "0<> AND," where 0<> is implemented as:<> (? -- ?) 0= 0= ; (or more efficiently with an in-nucleus solution.

Dick Burggraaff

Kwintsheul, The Netherlands

Jack responds: It is indeed as Mr. Buggraaff states: The semantic ambiguity of NOT caused in the past many Forth programmers to hand-roll unambiguous system extensions. It is this apparently irreconcilable ambiguity in the minds of the community which caused X3J14 to omit NOT from the draft proposed Standard and to leave in its place(s) the words 0= (boolean) and INVERT (bitwise).

And now to the concern expressed by William Higinbotham ("Letters," February 1992) that the required word set for an ANS Standard Forth system be of minimal size is most enthusiastically shared by the ANS X3/X3J14 Technical Committee. It is for this reason that the definition of an "ANS Forth Programming System" states that:

An ANS Forth System implements the entire Core word set as defined in this document. An ANS Forth system may also provide words from any of the optional word sets and extensions words sets, provided these words behave as described. [X3J14 dp-ANS2 4.1 (emphasis added)]

Mr. Higinbotham says, "I felt the standard should be on the Core Word Set." This is precisely the effect of the proposed Standard. I apologize if my article left this matter unclear, and thank Mr. Higinbotham for his comment.

Sound Off

Dear DDJ,

This is in response to Theron Wierenga's January, 1992 letter ("Looking for Free Speech"). I have tested a couple of speech-via-PC-speaker programs. They are SW-TALK, by Orlando Dare (CSS Inc., 3005 Glenmore Ave., Baltimore, MD 21214) and the December, 1991 Windows/DOS Developer's Journal article, "Writing for the PC Speaker," by Robert Bybee (5011 Broughham Ct., Stone Mountain, GA 30087).

Both perform pretty much the same on my system, although implementation may differ somewhat. (Dare is a bit tight-lipped about how he does it, but Bybee reveals all.) On my system, voice reproduction using either of them contains about the same amount of noise and distortion (readily noticeable), but speech is generally intelligible.

You really need a digital-to-analog converter in front of an amplifier/ speaker combination. The Speech Thing (COVOX Inc., 675-D Conger St., Eugene, OR 97402) produces clear speech with no noise or distortion. It even reads your text files to you in a pleasant, inflected voice. I highly recommend it.

Incidentally, Theron's use of the term "AM" is inappropriate; AM (amplitude modulation) refers to a method of impressing a radio-frequency carrier with audio (hence "AM radio"). I assume when he says "AM signal," he really means "analog audio" -- a bit redundant, as "digital audio" is really pulse-width modulation by audio.

He's right when he says that pulse-width modulation can (in principle) do a pretty good job of producing speech even when fed directly to a loudspeaker; but without a D-A converter, speech quality is highly dependent on the electro-mechanical characteristics for the speaker. Let's hope that the next generation of personal computers has a D-A converter ahead of a decent speaker!

Homer B. Tilton

Tucson, Arizona

Dear DDJ,

A few months ago I met a problem similar to Theron Wierenga's. I was wondering whether it was possible to port a SUNSPARC speech record coded with an 8-bit u-law format to the PC and let the better-than-none PC speaker play. In the beginning, I intended to use the same approach Theron did, but I could not find a solution.

The solution I finally found was this: Port 0x42 of PC is a frequency counter connected to the speaker. Instead of software filtering and generating 0s and 1s, you may as well let this job be done by hardware. All you need is to load the 8-bit AM value into this port and you are hands-free.

I know this is not the exact answer Theron wants, but I think it may be the ultimate solution given a bare PC without any add-on hardware.

Patrick Shu-Pui Ko

Hong Kong

Editor's note: Additional sources of PC sound that were brought to our attention are Real Sound from Access Software (4910 W. Amelia Erhart, Salt Lake City, UT 84116), TurboSound from Silicon Shack (5120 Campbell Ave., Suite 112, San Jose, CA 95130; 408-446-4521), and The Audio Solution (P.O. Box 11688 Clayton, MO 63105; 314-567-0267).

Hamming Had it First

Dear DDJ,

In the February 1992 "Programmer's Bookshelf" column, Andrew Schulman implies that Claude E. Shannon was the inventor of error detecting and correcting codes. It is a common misunderstanding. Although Shannon and Marcel J.E. Golay were both early researchers in the field, credit is clearly due to Richard Hamming of Bell Laboratories, whose first memorandum on the subject was dated July 27, 1947. Those who doubt may verify this in the first chapter of Thomas M. Thompson's From Error-correcting Codes Through Sphere Packings to Simple Groups (The Carus Mathematical Monographs, Volume 21).

Darren Allen

Atlanta, Georgia

UART Musings

Dear DDJ,

Part of Jeff Duntemann's column is nowadays devoted to UART programming. What a splendid subject! Many of us have at one time or another spent a lot of time finding out what all the various registers are supposed to do. The endless books one had to go through...

One time, while testing a program, I noticed that the transmission of characters could stop for some unknown error. After contact with the distributor, I received a leaflet pointing out some of the problems I ran into. It concerns Application note 493 from National Semi-conductor entitled, "A comparison of the INS8250, NS16450, and NS16550A series of UARTs." It gives a number of problems and suggests solutions for them. With it, some, but certainly not all of my problems were solved.

I'd be grateful if Jeff were to cover these issues in a forthcoming columns.

Alan Vlieger

Hillegom, The Netherlands

/*  Elik Listing 1: datetime.h

Header file for ctime2.c

*/

#ifndef DATE_TIME_H #define DATE_TIME_H

#define SECS_PER_MIN   60
#define SECS_PER_HOUR   (SECS_PER_MIN*60)
#define SECS_PER_DAY   (SECS_PER_HOUR*24L)   /* long constant! */
#define HOURS_PER_DAY   24
#define DAYS_PER_WEEK   7
#define DAYS_PER_YEAR   365
#define LEAP_PERIOD   (365*4+1)

#define BASE_YEAR   1970
#define UNTIL_1980   (365*10+2)            /* 01-01-1970 to 01-01-1980 */
#define UNTIL_FEB29   (365*2+31+29-1)       /* 01-01-1970 to 29-02-1972 */
#define UNTIL_2000   (365*30+7+31+28-1)    /* 01-01-1970 to 28-02-2000 */
#define THURSDAY   4
#define APRIL   (90-1)
#define OCTOBER   (273-1)

/*
Similar structure (struct tm) is defined in <time.h>, but I prefer to use my own - I think it makes code clearer.
(And, also, my structure is slightly shorter).
*/
struct date_time {
  short   yr,   /* year */
          mo,   /* month (0-11) */
          dy,   /* day */
          dw,   /* day of week (0-6) */
          hr,   /* hours */
          mn,   /* minutes */
          sc;   /* seconds */
};

typedef int boolean;
typedef unsigned short word;
typedef unsigned long dword;

char *ctime2 (const time_t *time);
time_t dt2time (struct date_time *dtp);

#endif


/* Elik Listing 2: ctime2.c

Corrected version of ctime by U.ELIK  --  DDJ #17 Jun 1991
'Using the Real-Time Clock' by Kenneth Roach p.26 listing one
p.88 )

*/

#include <stdio.h>
#include <time.h>
#include "datetime.h"

static word nearest_sunday (word days_yr, word year);

/*
Replacement to C library ctime() function. Working more than 300% (QuickC 2.01) faster than ctime().
*/
char *ctime2 (const time_t *tptr) {
  static char time_str[26] = "www mmm dd HH:MM:SS yyyy\n"; static char *months[12] = {
    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  }, *wdays[7] = {
    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static unsigned short month_days[] = {
      0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };

  struct date_time dt;    /* we must fill this structure
			     and then convert it to string */
  dword time;
  word days;
  register char *chptr;
  register word i;

  time = *tptr - timezone; /* because C time() function returns GMT value */
  days = (word)(time / SECS_PER_DAY); /* days since 1970 */

  if (days < UNTIL_1980)
    return NULL;         /* on dates before 1980, ctime() returns NULL */

  /* first, calculating time - it's simple */
  time %= SECS_PER_DAY; /* seconds this day */
  dt.hr = (word)(time / SECS_PER_HOUR);
  i = (word)(time % SECS_PER_HOUR); /* seconds this hour */
  dt.mn = i / SECS_PER_MIN;
  dt.sc = i % SECS_PER_MIN;
  
  /* day of week - it's simple too */
  dt.dw = (days + THURSDAY) % DAYS_PER_WEEK;
  
  /* then calculating date - it's little bit more complex */
  i = days - UNTIL_FEB29; /* days since Feb 29 1972 */
  days -= (i / LEAP_PERIOD + 1); /* delete all Feb 29s */
  /* now we can calculate everything as if leap years doesn't exist */

  dt.yr = days / DAYS_PER_YEAR + BASE_YEAR;
  if ((i % LEAP_PERIOD) == 0) { /* today Feb 29th special case */
    dt.mo = 1; /* Feb */
    dt.dy = 29;
  } else {
    days %= DAYS_PER_YEAR; /* days this year */
    if (daylight /* if a daylight-saving-time zone is specified */
	&& days >= nearest_sunday (dt.yr>1986 ? APRIL+1 : APRIL+24, dt.yr)
	&& days < nearest_sunday (OCTOBER+25, dt.yr) /* and this day belongs to summer period */
	/* (1980-1986: Apr 24 - Oct 25 */
	/* after 1986: Apr 1 - Oct 25) */
	) {
      /* then it's one hour later */
      if (++dt.hr == HOURS_PER_DAY) {
	days++;
	dt.dw = (dt.dw+1) % DAYS_PER_WEEK;
	dt.hr = 0;
      }
    }
    for (i=1; month_days[i] <= days; i++)
      ;
    dt.mo = --i; /* months must be 0 - 11 */ dt.dy = days - month_days[i] + 1;
  }

  /* now fill the string */

  /* To use sprintf() is the simpliest way (for programmer, of course),
     but it's executing VERY slowly, especially in QuickC and MSC.
     Code below may look cumbersome, but it's MUCH quicker.

     sprintf (time_str, "%3s %3s %02d %02d:%02d:%02d %04d\n",
             wdays[dt.dw], months[dt.mo], dt.dy, dt.hr, dt.mn, dt.sc, dt.yr);
  */

  chptr = wdays[dt.dw]; 
  time_str[0] = chptr[0];
  time_str[1] = chptr[1];
  time_str[2] = chptr[2];

  chptr = months[dt.mo];
  time_str[4] = chptr[0];
  time_str[5] = chptr[1];
  time_str[6] = chptr[2];

  time_str[8] = (char)(dt.dy / 10 + '0');
  time_str[9] = (char)(dt.dy % 10 + '0');

  time_str[11] = (char)(dt.hr / 10 + '0');
  time_str[12] = (char)(dt.hr % 10 + '0');

  time_str[14] = (char)(dt.mn / 10 + '0');
  time_str[15] = (char)(dt.mn % 10 + '0');

  time_str[17] = (char)(dt.sc / 10 + '0');
  time_str[18] = (char)(dt.sc % 10 + '0');

  time_str[20] = (char)(dt.yr / 1000 + '0');
  i = dt.yr % 1000;
  time_str[21] = (char)(i / 100 + '0');
  i %= 100;
  time_str[22] = (char)(i / 10 + '0');
  time_str[23] = (char)(i % 10 + '0');

  return time_str;
}

/* Returns nearest Sunday after (and including) given date. 
   <days_yr> must be AFTER February!
*/
static word nearest_sunday (word days_yr, word year) {
  word dwk, /* day of week (0 - 6) */
    yr=year-1970;   /* years since 1970 */

  dwk = ((days_yr + yr * DAYS_PER_YEAR + (yr+2) / 4)  /* days since 1970 */
	 - 1 + THURSDAY) % DAYS_PER_WEEK;
  return days_yr + ((DAYS_PER_WEEK-1)-dwk);
}

/* Elik Listing 3: compiler.h
   Macros to determine used compiler.
*/

#ifdef _QC
#define COMPILER "QuickC" #else
#ifdef _MSC_VER
#define COMPILER "MSC" #else
#ifdef __BORLANDC__
#define COMPILER "BorlandC++" #else
#ifdef __TURBOC__
#define COMPILER "TurboC" #else
#define COMPILER "Unknown compiler" #endif
#endif #endif
#endif

/* Elik Listing 4: cmpctim1.c
Test no.1 for ctime2() function by U.ELIK
*/

#include <stdio.h>
#include <time.h>
#include "datetime.h"
#include "compiler.h"

#define TEST_TICKS (5*18+1)

static unsigned long far *tickptr = (unsigned long far *)0x0000046C;

unsigned long test (char * (*fun) (const time_t * )) {
  unsigned long count=0, start;
  time_t t;

  time (&t);
  start = *tickptr;
  while (*tickptr == start)
    ;
  for (start+=(TEST_TICKS+1); *tickptr < start; t++) {
    (*fun) (&t); count++;
  }
  return count;
}

unsigned long percent (unsigned long c1, unsigned long c2) {
  unsigned long ret;
  c1 *= 100;
  ret = c1 / c2;
  if (c1 % c2 >= 50)
    ret++;
  return ret;
}

void main (int argc, char *argv[]) {
  unsigned long n1, n2;

  printf ("\nTesting %s code...\n", COMPILER);
  printf ("ctime2() called %ld times\n", n2=test (ctime2));
  printf ("ctime() called %ld times\n", n1=test (ctime));
  if (n1>n2)
    printf ("ctime() %ld%% faster than ctime2()\n", percent (n1, n2));
  else
    printf ("ctime2() %ld%% faster than ctime()\n", percent (n2, n1)); 
}

/* Elik Listing 5: cmpctim2.c
Test no.2 for ctime2() function by U.ELIK
*/

#include <stdio.h>
#include <time.h>
#include "datetime.h"
#include "compiler.h"

#define TEST_TICKS (5*18+1)

static unsigned long far *tickptr = (unsigned long far *)0x0000046C;

unsigned test (char * (*fun) (const time_t * )) {
  unsigned count=0;
  unsigned long start;
  time_t t;

  time (&t);
  start = *tickptr;
  while (*tickptr == start)
    ;
  for (start+=(TEST_TICKS+1); *tickptr < start; ) {
    (*fun) (&t); count++;
  }
  return count;
}

unsigned percent (unsigned long c1, unsigned long c2) {
  unsigned ret;
  c1 *= 100;
  ret = (unsigned)(c1 / c2);
  if (c1 % c2 >= 50)
    ret++;
  return ret;
}

void main (int argc, char *argv[]) {
  unsigned n1, n2;

  printf ("\nTesting %s code...\n", COMPILER);
  printf ("ctime2() called %u times\n", n2=test (ctime2));
  printf ("ctime() called %u times\n", n1=test (ctime));
  if (n1>n2)
    printf ("ctime() %u%% faster than ctime2()\n", percent (n1, n2));
  else
    printf ("ctime2() %u%% faster than ctime()\n", percent (n2, n1));
}


Copyright © 1992, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.