diff options
author | tomsmeding <tom.smeding@gmail.com> | 2016-06-05 23:36:35 +0200 |
---|---|---|
committer | tomsmeding <tom.smeding@gmail.com> | 2016-06-05 23:36:35 +0200 |
commit | 8131295c044eae68de2eb7465384edd56838dc4a (patch) | |
tree | 6f4639c8dfb77cde5a2e16a26e9ac9526dce6b2a |
First
-rw-r--r-- | Makefile | 15 | ||||
-rwxr-xr-x | number | bin | 0 -> 13512 bytes | |||
-rw-r--r-- | number.c | 189 |
3 files changed, 204 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2b814d4 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +CC = gcc +CFLAGS = -Wall -Wextra -O2 -std=c11 -fwrapv +BIN = number + +.PHONY: all clean remake + +all: $(BIN) + +clean: + rm -f $(BIN) + +remake: clean all + +$(BIN): $(wildcard *.c *.h) + $(CC) $(CFLAGS) -o $@ $(filter %.c,$^) Binary files differdiff --git a/number.c b/number.c new file mode 100644 index 0000000..27df086 --- /dev/null +++ b/number.c @@ -0,0 +1,189 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <inttypes.h> + +__attribute__((noreturn)) void usage(int argc,char **argv){ + (void)argc; + fprintf(stderr, + "Usage: %s [-co] <number>\n" + "Converts a number to (British) English word form.\n" + "Currently supports only integers.\n" + " -c Capitalise the first output letter\n" + " -o Write as ordinal (first) instead of cardinal number (one)\n", + argv[0]); + exit(1); +} + +char *thousands[6]={"thousand","million","billion","trillion","quadrillion","quintillion"}; +char *twenty[20]={ + "zero","one","two","three","four","five","six","seven","eight","nine","ten", + "eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen" +}; +char *tens[10]={ + "zero","ten","twenty","thirty","forty","fifty","sixty","seventy","eighty","ninety" +}; + +char *ordtwenty[20]={ + "zeroth","first","second","third","fourth","fifth","sixth","seventh","eighth","ninth","tenth", + "eleventh","twelfth","thirteenth","fourteenth","fifteenth","sixteenth","seventeenth","eighteenth","nineteenth" +}; +char *ordtens[10]={ + "zeroth","tenth","twentieth","thirtieth","fortieth","fiftieth","sixtieth","seventieth","eightieth","ninetieth" +}; + +void addtobuf(char *text,char **buf){ + int len=strlen(text); + strcpy(*buf,text); + *buf=*buf+len; +} + +char* printnumber(intmax_t num,char *buf){ + if(num<0){ + addtobuf("minus ",&buf); + num=-num; + } + + bool hadth=false; + if(num>=1000){ + hadth=true; + intmax_t th[6],nth=0,rest; + rest=num%1000; + num/=1000; + while(num){ + th[nth++]=num%1000; + num/=1000; + } + bool first=true; + while(nth-->0){ + if(th[nth]==0)continue; + if(!first)addtobuf(" ",&buf); + buf=printnumber(th[nth],buf); + addtobuf(" ",&buf); + addtobuf(thousands[nth],&buf); + first=false; + } + num=rest; + if(num==0)return buf; + addtobuf(" ",&buf); + } + + bool and=(num>=100||hadth)&&num%100!=0; + if(num>=100){ + buf=printnumber(num/100,buf); + addtobuf(" ",&buf); + addtobuf("hundred",&buf); + num%=100; + if(num)addtobuf(" ",&buf); + else return buf; + } + + if(and)addtobuf("and ",&buf); + + if(num%10==0)addtobuf(tens[num/10],&buf); + else if(num<20)addtobuf(twenty[num],&buf); + else { + addtobuf(tens[num/10],&buf); + addtobuf("-",&buf); + addtobuf(twenty[num%10],&buf); + } + return buf; +} + +char* printordinal(intmax_t num,char *buf){ + if(num<0){ + addtobuf("minus ",&buf); + num=-num; + } + + bool hadth=false; + if(num>=1000){ + hadth=true; + buf=printnumber(num/1000*1000,buf); + num%=1000; + if(num==0){ + addtobuf("th",&buf); + return buf; + } + addtobuf(" ",&buf); + } + + if(hadth&&num/100==0&&num%100!=0)addtobuf("and ",&buf); + + if(num<20){ + addtobuf(ordtwenty[num],&buf); + return buf; + } + + if(num<100){ + if(num%10==0)addtobuf(ordtens[num/10],&buf); + else { + addtobuf(tens[num/10],&buf); + addtobuf("-",&buf); + addtobuf(ordtwenty[num%10],&buf); + } + return buf; + } + + buf=printnumber(num/100*100,buf); + if(num%100==0){ + addtobuf("th",&buf); + return buf; + } + addtobuf(" and ",&buf); + return printordinal(num%100,buf); +} + +int main(int argc,char **argv){ + char *numstr=NULL; + bool ordinal=false,capfirst=false; + for(int i=1;i<argc;i++){ + if(argv[i][0]=='\0'){ + fprintf(stderr,"Unexpected empty argument\n"); + usage(argc,argv); + } + if(argv[i][0]=='-'){ + for(int j=1;argv[i][j];j++){ + switch(argv[i][j]){ + case 'c': capfirst=true; break; + case 'o': ordinal=true; break; + default: + fprintf(stderr,"Unrecognised flag -%c\n",argv[i][j]); + usage(argc,argv); + } + } + } else { + if(numstr==NULL)numstr=argv[i]; + else { + fprintf(stderr,"Unexpected second bare argument '%s'\n",argv[i]); + usage(argc,argv); + } + } + } + if(numstr==NULL){ + fprintf(stderr,"No number given\n"); + usage(argc,argv); + } + + char *endp; + intmax_t num=strtoimax(numstr,&endp,10); + if(*endp!='\0'){ + fprintf(stderr,"Invalid number: '%s'\n",numstr); + return 1; + } + char *repr; + asprintf(&repr,"%jd",num); + if(strcmp(numstr,repr)!=0){ + fprintf(stderr,"Outside range of %lu-bit number: '%s'\n",8*sizeof(intmax_t),numstr); + return 1; + } + + char buf[300]={'\0'}; + + if(ordinal)printordinal(num,buf); + else printnumber(num,buf); + if(capfirst)buf[0]-='a'-'A'; + printf("%s\n",buf); + return 0; +} |