I'm a bit worried about my scanf usage in main(), it appears to work fine
but looks really weird. It allows stuff like the input line "get 23 and 34
then quit", from which it will interpret 23, 34, then the 'q' will make it
quit.
Some tips or corrections would be handy. (It's not an assignment, just
practise.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* convert number to words */
char *numtoword (char *dest, int num)
{
char *units[] =
{
"zero", "one", "two", "three", "four", "five",
"six", "seven", "eight", "nine"
};
char *teens[] =
{
"ten", "eleven", "twelve", "thirteen", "fourteen",
"fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
};
char *tens[] =
{
"", "", "twenty", "thirty", "forty", "fifty",
"sixty", "seventy", "eighty", "ninety"
};
*dest = '\0';
if (num<0)
{
strcat (dest, "minus ");
num = -num;
}
if (num > 999)
{
strcat (dest, "too big");
return dest;
}
if (num > 99)
{
strcat (dest, units[num / 100]);
strcat (dest, " hundred");
num = num % 100;
if (num != 0)
strcat(dest, " and ");
else
return dest;
}
if (10<=num && num<=19)
strcat(dest, teens[num % 10]);
else
{
strcat(dest, tens[num / 10]);
if (num%10 != 0 || num==0)
{
if (num > 20)
strcat(dest, "-");
strcat(dest, units[num % 10]);
}
}
return dest;
}
int main (void)
{
int i;
char words[100];
puts ("Enter integers, or q to exit.");
for(;;)
{
while (1 != scanf("%d", &i))
{
int c = getchar();
if (c == 'q' || c == EOF)
return 0;
}
if (numtoword(words, i))
printf ("%s\n", words);
else
break;
}
return 0;
}
--
Simon.
> char *numtoword (char *dest, int num)
> {
(snip)
> *dest = '\0';
>
> if (num<0)
> {
> strcat (dest, "minus ");
> num = -num;
> }
>
> if (num > 999)
> {
> strcat (dest, "too big");
> return dest;
> }
This will fail on many machines if num equals INT_MIN.
INT_MIN is a negative number, but (-INT_MIN) is NOT usually
representable as an int on a two's complement machine, and
in such a case, computing (-INT_MIN) produces undefined
behavior. Hence, I would replace the code abovce with
something like this:
*dest = '\0';
if (num > 999 || num < -999)
{
strcat (dest, "magnitude too big");
return dest;
}
if (num<0)
{
strcat (dest, "minus ");
num = -num;
}
As regards the following:
> if (numtoword(words, i))
> printf ("%s\n", words);
> else
> break;
Your function "numtoword" always returns a non-null pointer, so
it's not clear to me what this test is for. Is this intended
to handle a future modification where "numtoword" would return
NULL (as opposed to "too big") if it received an input that was
too large?
Thanks, that's a good catch. My new code catches that (it's the only thing
it catches now).
> Your function "numtoword" always returns a non-null pointer, so
> it's not clear to me what this test is for. Is this intended
> to handle a future modification where "numtoword" would return
> NULL (as opposed to "too big") if it received an input that was
> too large?
Originally I did make it return NULL when it was out of range. But for
testing purposes I wanted to be able to see what had been put in the string
already even if it did bail out, so I decided to return dest anyway.
But now it only returns NULL if you give a negative number which can't be
negated.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <limits.h>
/* convert number to words */
static void add0to999(char *dest, long num)
{
char *units[] =
{
"zero", "one", "two", "three", "four", "five",
"six", "seven", "eight", "nine"
};
char *teens[] =
{
"ten", "eleven", "twelve", "thirteen", "fourteen",
"fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
};
char *tens[] =
{
"", "", "twenty", "thirty", "forty", "fifty",
"sixty", "seventy", "eighty", "ninety"
};
if (num > 99)
{
strcat (dest, units[num / 100]);
strcat (dest, " hundred");
num = num % 100;
if (num != 0)
strcat(dest, " and ");
else
return;
}
if (10<=num && num<=19)
strcat(dest, teens[num % 10]);
else
{
strcat(dest, tens[num / 10]);
if (num%10 != 0 || num==0)
{
if (num > 20)
strcat(dest, "-");
strcat(dest, units[num % 10]);
}
}
}
char *numtowords (char *dest, long num)
{
char *mult[] = {" billion ", " million ", " thousand "};
int i, n = sizeof mult / sizeof *mult;
/* We can't negate LONG_MIN if it's less than -LONG_MAX */
if (num < -LONG_MAX) return NULL;
/* Empty string */
*dest = '\0';
/* Handle negative numbers */
if (num<0)
{
strcat (dest, "minus ");
num = -num;
}
/* Iterate through multipliers */
for (i=0; i<n; i++)
{
long x = pow(1000, n-i);
if (num >= x)
{
add0to999(dest, num/x);
strcat(dest, mult[i]);
num = num % x;
if (num == 0) return dest;
if (num < 99) strcat(dest, "and ");
}
}
/* The final 3 digits */
add0to999(dest, num);
return dest;
}
int main (void)
{
long i;
char words[200];
puts ("Enter integers, or q to exit.");
for(;;)
{
while (1 != scanf("%ld", &i))
{
int c = getchar();
if (c == 'q' || c == EOF)
return 0;
}
if (numtowords(words, i))
printf ("%s\n", words);
else
printf ("Magnitude too large\n");
}
return 0;
}
--
Simon.
[snip - code for num2text]
Your program is pretty good. The only complaint I have about it is that
"987" should be "nine hundred eighty-seven" and not "nine hundred AND
eighty-seven." At least this is what I learned in school, although
common usage might be otherwise.
I had written a similar program like this myself, but the goal was to
translate dollar amounts to text (for speech synthesis?). To get around
the problem of knowing how large the number is (i.e., powers of ten), I
build the text string from left to right by reversing the text at each
step and then reversing the final result. The function amount() does
this, but as I look back on it, I wish I had commented a little more;
it's a bit intricate.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUL '\0'
#define ZERO ones[0]
#define ONE ones[1]
enum {
MAXDIGITS = 15 /* largest whole currency amount */
};
char *reverse(char *);
char *amount(char *);
int valid(char *);
char *ones[] = {
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
"eleven",
"twelve",
"thirteen",
"fourteen",
"fifteen",
"sixteen",
"seventeen",
"eighteen",
"nineteen",
};
char *tens[] = {
0,
0,
"twenty",
"thirty",
"forty",
"fifty",
"sixty",
"seventy",
"eighty",
"ninety",
};
char *hundreds[] = {
" ",
" thousand",
" million",
" billion",
" trillion",
};
char zeros[MAXDIGITS+1];
char *argv0;
void
usage(void)
{
fprintf(stderr, "usage: %s amount...\n", argv0);
exit(1);
}
int
valid(char *amt)
{
size_t n;
char *p, *e;
/* bad decimal amount or size */
e = amt + strlen(amt);
if(p=strrchr(amt, '.')){
if(e - p != 3)
return 0;
if(p - amt > MAXDIGITS)
return 0;
}else if(e - amt > MAXDIGITS)
return 0;
/* bad character */
while(n=strspn(amt, "1234567890."))
amt += n;
return *amt == 0;
}
char *
reverse(char *s)
{
static char buf[BUFSIZ];
char *e, *p;
p = buf;
e = s + strlen(s) - 1;
while(e >= s)
*p++ = *e--;
*p = NUL;
return buf;
}
char *
amount(char *amt)
{
char buf[BUFSIZ]={0};
char *p;
size_t n; /* numerals to process */
int v, /* value */
h=0, /* hundred? */
m=0; /* magnitude */
int k, offset[] = {2, 1, 1};
n = strlen(amt);
/* return "zero" on all zeros */
if(!strncmp(amt, zeros, n))
return ones[0];
p = amt + n;
while(n){
k = offset[(n == 1) + h];
p -= k;
if(h % 2){
v = atoi(p) % 10;
if(v)
strcat(buf, reverse(" hundred"));
m++;
h=0;
}else{
strcat(buf, reverse(hundreds[m]));
v = atoi(p) % 100;
h=1;
}
*p = NUL;
n -= k;
if(v){
if(v < 20)
strcat(buf, reverse(ones[v]));
else{
if(v%10){
strcat(buf, reverse(ones[v%10]));
strcat(buf, "-");
}
strcat(buf, reverse(tens[v/10]));
}
if(n)
strcat(buf, " ");
}
}
return reverse(buf+1);
}
int
main(int argc, char *argv[])
{
char *p, *s;
char buf[BUFSIZ];
argv0 = argv[0];
if(argc == 1)
usage();
memset(zeros, '0', sizeof zeros - 1);
zeros[sizeof zeros - 1] = NUL;
for(argv++; *argv; argv++){
if(valid(*argv)){
strcpy(buf, *argv);
if(p=strrchr(buf, '.'))
*p = NUL;
s = p == buf ? ZERO : amount(buf);
printf("%s dollar%s", s, strcmp(s, ONE) ? "s" : "");
s = p == 0 ? ZERO : amount(p+1);
printf(" and %s cent%s\n", s, strcmp(s, ONE) ? "s" : "");
}else
fprintf(stderr, "invalid: %s\n", *argv);
}
return 0;
}
david
--
If 91 were prime, it would be a counterexample to your conjecture.
-- Bruce Wheeler
I believe this is a regional difference. At school here in Australia I was
taught it should be "nine hundred and eighty-seven". Often we shorten it to
"nine eighty-seven" but we never have "hundred" without "and".
> I had written a similar program like this myself, but the goal was to
> translate dollar amounts to text (for speech synthesis?). To get around
> the problem of knowing how large the number is (i.e., powers of ten), I
> build the text string from left to right by reversing the text at each
> step and then reversing the final result. The function amount() does
> this, but as I look back on it, I wish I had commented a little more;
> it's a bit intricate.
[snip code]
It's bugged... If there is nothing in a position you should leave out the
qualifier!
C:\clc\drubin>a 1000000000
one billion million thousand dollars and zero cents
(should be one billion dollars)
C:\clc\drubin>a 1000000
one million thousand dollars and zero cents
(should be one million dollars)
--
Simon.
> [snip code]
>
> It's bugged... If there is nothing in a position you should leave out the
> qualifier!
>
> C:\clc\drubin>a 1000000000
> one billion million thousand dollars and zero cents
> (should be one billion dollars)
>
> C:\clc\drubin>a 1000000
> one million thousand dollars and zero cents
> (should be one million dollars)
Quite right! Thanks.
No more accurate than LDBL_DIG significant digits.
fmtmoney 999999999.88
fmt_money(1e+009) = Nine Hundred Ninety-Nine Million Nine Hundred
Ninety-Nine Thousand Nine Hundred Ninety-Nine & 88/100
*/
/* +++Date last modified: 02-Nov-1995 */
/*
** FMTMONEY.C - Format a U.S. dollar value into a numeric string
**
** public domain demo by Bob Stout
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>
#define Form(s,a) bufptr += sprintf(bufptr, s, a)
static char buf[1024],
*bufptr;
static const char *units[] = {
"Zero", "One", "Two", "Three", "Four",
"Five", "Six", "Seven", "Eight", "Nine",
"Ten", "Eleven", "Twelve", "Thirteen", "Fourteen",
"Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"
};
static const char *tens[] = {
"Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty",
"Ninety"
};
typedef struct tag_DigitGroups {
long double dpower;
char *pszName;
} DigitGroups;
static const DigitGroups PowersOfOneThousand[] = {
{1e00, ""},
{1e03, "Thousand"},
{1e06, "Million"},
{1e09, "Billion"},
{1e12, "Trillion"},
{1e15, "Quadrillion"},
{1e18, "Quintillion"},
{1e21, "Sextillion"},
{1e24, "Septillion"},
{1e27, "Octillion"},
{1e30, "Nonillion"},
{1e33, "Decillion"},
{1e36, "Undecillion"},
{1e39, "Duodecillion"},
{1e42, "Tredecillion"},
{1e45, "Quattuordecillion"},
{1e48, "Quindecillion"},
{1e51, "Sexdecillion"},
{1e54, "Septendecillion"},
{1e57, "Octodecillion"},
{1e60, "Novemdecillion"},
{1e63, "Vigintillion"}
};
static void form_group(int, char *);
/*
** Call with long double amount
** Rounds cents
** Returns string in a static buffer
*/
char *fmt_money(long double amt)
{
int temp;
int i;
int topgroup;
long double dummy;
long double cents = modfl(amt, &dummy);
long double powerof10;
*buf = '\0';
bufptr = buf;
if (amt < 0) {
amt = -amt;
*bufptr = '-';
bufptr++;
*bufptr = 0;
}
powerof10 = log10l(amt);
topgroup = ((int) powerof10) / 3;
if (topgroup > 21) {
puts("Error! Unable to process numbers that large.\n");
exit(EXIT_FAILURE);
}
if (powerof10 > LDBL_DIG) {
puts("Warning! Loss of accuracy in calculation.\n");
}
for (i = topgroup; i > 0; i--) {
temp = (int) (amt / PowersOfOneThousand[i].dpower);
if (temp) {
form_group(temp, PowersOfOneThousand[i].pszName);
amt = fmodl(amt, PowersOfOneThousand[i].dpower);
}
}
form_group((int) amt, "");
if (buf == bufptr)
Form("%s ", units[0]);
temp = (int) (cents * 100. + .5);
sprintf(bufptr, "& %02d/100", temp);
return buf;
}
/*
** Process each thousands group
*/
static void form_group(int amt, char *scale)
{
if (buf != bufptr)
*bufptr++ = ' ';
if (100 <= amt) {
Form("%s Hundred ", units[amt / 100]);
amt %= 100;
}
if (20 <= amt) {
Form("%s", tens[(amt - 20) / 10]);
if (0 != (amt %= 10)) {
Form("-%s ", units[amt]);
} else
Form("%s", " ");
} else if (amt) {
Form("%s ", units[amt]);
}
Form("%s", scale);
}
#ifdef TEST
main(int argc, char *argv[])
{
while (--argc) {
long double amt = atof(*(++argv));
printf("fmt_money(%Lg) = %s\n", amt, fmt_money(amt));
}
return EXIT_SUCCESS;
}
#endif /* TEST */