How output a numbers with write() (only #include <unistd.h> allowed) [duplicate] - c

It is possible to convert integer to string in C without sprintf?

There's a nonstandard function:
char *string = itoa(numberToConvert, 10); // assuming you want a base-10 representation
Edit: it seems you want some algorithm to do this. Here's how in base-10:
#include <stdio.h>
#define STRINGIFY(x) #x
#define INTMIN_STR STRINGIFY(INT_MIN)
int main() {
int anInteger = -13765; // or whatever
if (anInteger == INT_MIN) { // handle corner case
puts(INTMIN_STR);
return 0;
}
int flag = 0;
char str[128] = { 0 }; // large enough for an int even on 64-bit
int i = 126;
if (anInteger < 0) {
flag = 1;
anInteger = -anInteger;
}
while (anInteger != 0) { 
str[i--] = (anInteger % 10) + '0';
anInteger /= 10;
}
if (flag) str[i--] = '-';
printf("The number was: %s\n", str + i + 1);
return 0;
}

Here's an example of how it might work. Given a buffer and a size, we'll keep dividing by 10 and fill the buffer with digits. We'll return -1 if there is not enough space in the buffer.
int
integer_to_string(char *buf, size_t bufsize, int n)
{
char *start;
// Handle negative numbers.
//
if (n < 0)
{
if (!bufsize)
return -1;
*buf++ = '-';
bufsize--;
}
// Remember the start of the string... This will come into play
// at the end.
//
start = buf;
do
{
// Handle the current digit.
//
int digit;
if (!bufsize)
return -1;
digit = n % 10;
if (digit < 0)
digit *= -1;
*buf++ = digit + '0';
bufsize--;
n /= 10;
} while (n);
// Terminate the string.
//
if (!bufsize)
return -1;
*buf = 0;
// We wrote the string backwards, i.e. with least significant digits first.
// Now reverse the string.
//
--buf;
while (start < buf)
{
char a = *start;
*start = *buf;
*buf = a;
++start;
--buf;
}
return 0;
}

Unfortunately none of the answers above can really work out in a clean way in a situation where you need to concoct a string of alphanumeric characters.There are really weird cases I've seen, especially in interviews and at work.
The only bad part of the code is that you need to know the bounds of the integer so you can allocate "string" properly.
In spite of C being hailed predictable, it can have weird behaviour in a large system if you get lost in the coding.
The solution below returns a string of the integer representation with a null terminating character. This does not rely on any outer functions and works on negative integers as well!!
#include <stdio.h>
#include <stdlib.h>
void IntegertoString(char * string, int number) {
if(number == 0) { string[0] = '0'; return; };
int divide = 0;
int modResult;
int length = 0;
int isNegative = 0;
int copyOfNumber;
int offset = 0;
copyOfNumber = number;
if( number < 0 ) {
isNegative = 1;
number = 0 - number;
length++;
}
while(copyOfNumber != 0)
{
length++;
copyOfNumber /= 10;
}
for(divide = 0; divide < length; divide++) {
modResult = number % 10;
number = number / 10;
string[length - (divide + 1)] = modResult + '0';
}
if(isNegative) {
string[0] = '-';
}
string[length] = '\0';
}
int main(void) {
char string[10];
int number = -131230;
IntegertoString(string, number);
printf("%s\n", string);
return 0;
}

You can use itoa where available. If it is not available on your platform, the following implementation may be of interest:
https://web.archive.org/web/20130722203238/https://www.student.cs.uwaterloo.ca/~cs350/common/os161-src-html/atoi_8c-source.html
Usage:
char *numberAsString = itoa(integerValue);
UPDATE
Based on the R..'s comments, it may be worth modifying an existing itoa implementation to accept a result buffer from the caller, rather than having itoa allocate and return a buffer.
Such an implementation should accept both a buffer and the length of the buffer, taking care not to write past the end of the caller-provided buffer.

int i = 24344; /*integer*/
char *str = itoa(i);
/*allocates required memory and
then converts integer to string and the address of first byte of memory is returned to str pointer.*/

Related

Converting negative decimal to two's complement in C

I'm writing a program that, assuming the input is always a valid negative decimal integer, returns the two's complement binary representation (16 bit).
My logic here is that I take in inputs from the command line, and convert that with a simple conversion to binary and add them to the initialized binary array. Then, I take the one's complement (just change 0's to 1's and vise versa) and put that in the onesCom array. However, for the adding 1 part to find the two's complement, I think this is where the issue is but I'm struggling to find it. I am performing binary addition to the least significant bit.
When converting from one-complement to two-complement, i.e. adding 1, your loop should start from the LSB, not from the MSB.
Therefore,
for (j=15; j>=0; j--) { // <-- Error Here
if (onesCom[j] == 1 && carryOver == 1) {
twosCom[j] = 0;
} else if (onesCom[j] == 0 && carryOver == 1) {
twosCom[j] = 1;
carryOver = 0;
} else {
twosCom[j] = onesCom[j];
}
}
Should be replaced by:
for (j=0; j<=15; j++) {
if (onesCom[j] == 1 && carryOver == 1) {
twosCom[j] = 0;
} else if (onesCom[j] == 0 && carryOver == 1) {
twosCom[j] = 1;
carryOver = 0;
} else {
twosCom[j] = onesCom[j];
}
}
In your code, you calculate the one-complement then deduce the two-complement. Please note that it is easier to directly calculate the two-complement, in case you don't need the one-complement, like this:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
int binary[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
if (argc == 1) return 1;
int decimal = atoi(argv[1]);
int counter = 0;
if (decimal > -32768 && decimal < 0) {
decimal = 65536 + decimal;
while(decimal > 0) {
binary[counter] = decimal%2;
decimal = decimal/2;
counter++;
}
for (int length = 15; length >=0; length--) {
printf("%d", binary[length]);
}
printf ("\n");
}
return 0;
}
As your snippet is completely blurred, I can only suggest you two approaches to the problem:
The first assuming you are doing two's complement arithmethic all the time, in which case the digit adding must be done with sign.
The second assuming you only parse unsigned values and retaining the sign to make the sign exchange at the end.
Probably both approaches will lead to almost the same efficiency and be compiled into very similar code. I have no preference for any of them.
int decode(char *str, int base)
{
int result = 0,
c,
neg = FALSE;
/* skip whitespace, delete this if you don't
* want to cope with whitespace */
for (; isspace(c = *str); str++) {
continue;
}
if (*str == '-') {
neg = TRUE; /* negative */
str++; /* skip it */
}
/* the next characters might be all digits */
for (; isdigit(c = *str); str++) {
/* multiply by the base */
result *= base;
/* add positive for positives and
* subtract it for negatives */
int d = c - '0'; /* convert c to the digit value */
/* negative if number is negative */
if (neg) d = -d;
/* and add/subtract it */
result = result + d;
}
/* :) got it!! */
return result;
}
and the second approach is:
int decode(char *str, int base)
{
int result = 0,
c,
neg = FALSE;
/* skip whitespace, delete this if you don't
* want to cope with whitespace */
for (; isspace(c = *str); str++) {
continue;
}
if (*str == '-') {
neg = TRUE; /* negative */
str++; /* skip it */
}
/* the next characters might be all digits */
for (; isdigit(c = *str); str++) {
/* multiply by the base */
result *= base;
/* add positive for positives and
* subtract it for negatives */
int d = c - '0'; /* convert c to the digit value */
/* and add/subtract it */
result = result + d;
}
/* :) got it!! */
return neg ? -result : result;
}
Can you see the differences? (hint, I have eliminated one line in the loop and changed one line at the end :) )
If you want to run this code in a full, complete and verifiable example, there's one below, just put one of the above functions in place of the other, and run it.
#include <stdio.h>
#include <ctype.h>
/* these macros are for easy printing, and outputting the file, line and
* function name where the trace is being made */
#define F(_f) __FILE__":%d:%s:"_f, __LINE__, __func__
#define P(_f, ...) printf(F(_f), ##__VA_ARGS__)
/* I use these for portability, as <stdbool.h> is not always available */
#define FALSE (0)
#define TRUE (!FALSE)
int decode(char *str, int base)
{
/* substitute here the body of the function above you want to test */
}
int main()
{
static char *tests[] = {
"0", "-1", "-210", "-211", "-222", "1",
"210", "211", "222", "5400",
/* add more testing cases to your wish */
NULL,
};
int i, passed = 0;
for (i = 0; tests[i]; i++) {
char *test = tests[i];
int expected, actual;
P("Testing '%s' conversion\n", test);
/* expected, decoded with system routines */
if (sscanf(test, "%i", &expected) != 1) {
P("problem scanning %s\n", test);
continue;
}
/* actual, decoded with our function */
actual = decode(test, 10);
char *operator = actual == expected ? "==" : "!=";
P("Test result: actual(%i) %s expected(%i)\n",
actual, operator, expected);
if (actual == expected)
passed++;
}
P("passed %d/%d tests\n", passed, i);
}
Edit
The following code will allow you to easily convert your value to binary:
#define CHK(_n) ((_n) <= sz)
char *to_binary(int p_val, char *buf, size_t sz)
{
CHK(2); /* at least two bytes of buffer space */
buf += sz; /* we start from the end, backwards to avoid having to use
* one bit masks moving all the time around */
*--buf = '\0'; /* this is the last '\0' that should end the string */
sz--; /* update buffer size */
/* we operate better with unsigned, as the
* sign doesn't get involved in shifts (we are reinterpreting
* the sign bit as a normal bit, which makes the assumption that
* integers are stored in two's complement. This is essentially
* nonportable code, but it will work in the stated assumptions. */
unsigned val = (unsigned) p_val;
/* the first below is the second char we check
* above */
do {
*--buf = val & 1 ? '1' : '0';
sz--;
val >>= 1;
} while (CHK(1) && val);
return buf; /* return what we have */
}
And the final main() code looks like this:
int main()
{
static char *tests[] = {
"0", "-1", "-210", "-211", "-222", "1",
"210", "211", "222", "5400",
NULL,
};
int i, passed = 0;
for (i = 0; tests[i]; i++) {
char *test = tests[i];
int expected, actual;
P("Testing '%s' conversion\n", test);
/* expected, decoded with system routines */
if (sscanf(test, "%i", &expected) != 1) {
P("problem scanning %s\n", test);
continue;
}
/* actual, decoded with our function */
actual = decode(test, 10);
char *operator = actual == expected ? "==" : "!=";
char buff[100]; /* temporary variable to hold the
* converted value to binary */
P("Test result: actual(%i/0b%s)\n",
actual,
to_binary(actual, buff, sizeof buff));
P(" %s expected(%i/0b%s)\n",
operator,
expected,
to_binary(expected, buff, sizeof buff));
if (actual == expected)
passed++;
}
P("passed %d/%d tests\n", passed, i);
}

Ascii to integer using no library functions

My teacher gave us an assignment where we create a function that reads ASCII digit characters and converts them to a number without using any library functions such as atoi. Through some research i came up with this in my own file:
#include <stdio.h>
#include <sttdef.h>
int main() {
char testString[] = "123";
int convertedResult = 0;
int i;
for(i = 0; testString[i] != '\0'; i++){
convertedResult = convertedResult*10 + testString[i] - '0';
printf("%i\n",convertedResult);
if (testString[i] == '\0') {
break;
}
}
return 0;
}
While this works on its own i have to use the main file he gave us to call on this specific function.
char *asciiToInteger(char *inputString, int *integerPtr) {
return inputString;
}
I'm a bit confused as to how to proceed from here? attatched picture is main
#include <stdio.h>
#include <stddef.h>
char * asciiToInteger(char *inputString, int *integerPtr){
int convertedResult =0;
for(int i = 0; inputString[i] != '\0'; i++){
convertedResult = convertedResult*10 + inputString[i] - '0';
}
*integerPtr=convertedResult;
return inputString;
}
int main() {
char testString[] = "123";
int integerPtr;
asciiToInteger(testString, &integerPtr) ;
printf("%d\n",integerPtr);
return 0;
}
Your code has a couple of problems:
It assumes the entire string is digits
It checks for the end of string twice
I think a better implementation would be:
#include <stdio.h>
const char *asciiToInteger(const char *inputString, int *value)
{
int result = 0;
while (isdigit((unsigned int) *inputString))
{
result *= 10;
result += *inputString++ - '0';
}
*value = result;
return inputString;
}
This returns a pointer to the first non-converted character, which might be to the end of string marker if the string is all digits. I added const on the strings of course, since this converter is just reading from the strings.
When you get an assignment like this the first step is to make sure you understand what the function is supposed to do. Your question has no such description so that is the place to start.
From the behavior of the main function it seems to be something like:
If the first character in the input string is not a digit return NULL
If the first character in the input string is a digit convert all leading digits to an integer stored in the object pointed to by integerPtr and return a pointer to the character following the converted digits.
Examples:
inputString = "a123b67" --> return NULL
inputString = "123b67" --> *integerPtr = 123 and return a pointer to the 'b' in the input
That could look something like this:
char *asciiToInteger(char *inputString, int *integerPtr) {
if (*inputString < '0' || *inputString > '9')
return NULL; // no leading digit
*integerPtr = 0;
do
{
*integerPtr = *integerPtr * 10 + *inputString - '0';
++inputString;
} while (*inputString >= '0' && *inputString <= '9');
return inputString;
}
Notice that the code above can't handle negative integers.

Adding operation between two Big numbers by only using C

What I want to try is Adding two big numbers under 600 digits.
So I making a struct in C.
But there is some error in the source below.
(The environment of practice is GCC Compiler, and Linux. The tool is VSCode with BASH Terminal.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define MAX_SIZE 600
#define SWAP(x,y,t) ((t)=(x), (x)=(y), (y)=(t)) //SWAP preprocessor
#define D_C(x) (x==0 ? 0 : x+'0') //Convert Decimal to Character
#define C_D(x) (x==0 ? 0 : x-'0') //Convert Character to Decimal
/*The structure to save BIG NUMBER*/
typedef struct _BIG_DECIMAL{
unsigned char *data;
int size;
} BIG_DECIMAL;
/*Make string reverse*/
void reverseString(char* s, size_t size) {
char temp;
for (size_t i = 0; i < size / 2; i++) SWAP(s[i], s[(size - 1) - i], temp);
}
/*Create Decimal data in BIG_DECIMAL struct*/
BIG_DECIMAL * createDecimal(unsigned char *str) {
//local variables in func.
size_t size_str;
BIG_DECIMAL * number = malloc(sizeof(BIG_DECIMAL));
//save str in buffer
char buffer[MAX_SIZE] = {'\0',};
strcpy(buffer, str);
//temporary value for size measure.
size_str = strlen(buffer);
printf("%d", size_str);
//Save reversed number data.
reverseString(buffer, size_str);
strcpy(number->data, buffer);
//Save size.
number->size = size_str;
//Return BIG_DECIMAL struct.
return number;
}
/*ADDITION BETWEEN TWO BIG NUMBERS. left argument's size value should be big.*/
BIG_DECIMAL * BD_addition(BIG_DECIMAL *dec1, BIG_DECIMAL *dec2) {
//local variable in this func.
int carry = 0;
BIG_DECIMAL *result = malloc(sizeof(BIG_DECIMAL));
//Adding loop start
for(int i = 0; i < (result -> size); i++) {
int digit_plus;
//if start
if(i < dec2->size) {
//there are digit in both dec so...
digit_plus = C_D(dec1->data[i]) + C_D(dec2->data[i]) + carry;
//nested-if start
if(digit_plus > 10) { //if the carry is occured
carry = digit_plus / 10; //carry can be (> 1)
result->data[i] = D_C(digit_plus % 10);
}
else { //if the carry is not occcured
carry = digit_plus / 10; //carry can be (> 1)
result->data[i] = D_C(digit_plus % 10);
}
//nested-if end
}
else if((i >= (dec2->size)) && (i < ((result->size)-1))){
digit_plus = C_D(dec1->data[i]) + carry;
//nested-if start
if(digit_plus > 10) { //if the carry is occured
carry = digit_plus / 10;
result->data[i] = D_C(digit_plus % 10);
}
else { //if the carry is not occcured
carry = 0;
result->data[i] = D_C(digit_plus);
}
//nested-if end
}
else { //if i == (result->size)-1 (the last index of result->data)
//nested-if start
if(carry > 0) result->data[i] = D_C(carry); //if carry occured
else { //if the carry doesn't occure in the last index of result->data
result->data[i] = D_C(0); //the last index value of result->data is NULL.
--(result->size); //result size - 1
}
//nested-if end
}
//if end
}
//Adding loop end
return result;
}
int main() {
/*data for operand*/
BIG_DECIMAL * op1;
BIG_DECIMAL * op2;
/*data for result*/
BIG_DECIMAL * result;
op1 = createDecimal("123456789");
op2 = createDecimal("12345678");
result = BD_addition(op1,op2);
printf("%s", result->data);
/*DeAllocation*/
free(op1);
free(op2);
free(result);
return 0;
}
This code makes Segmentation fault error.
I think that it might be a string access error first, so I tried to type-casting all of the char* type variable but it doesn't work.
As pointer in comments, you can correct your code by allocating enough space for data, you can use strdup for this:
/*Create Decimal data in BIG_DECIMAL struct*/
BIG_DECIMAL * createDecimal(unsigned char *str) {
//local variables in func.
size_t size_str;
BIG_DECIMAL * number = malloc(sizeof(BIG_DECIMAL));
//save str in buffer
char buffer[MAX_SIZE] = {'\0',};
strcpy(buffer, str);
//temporary value for size measure.
size_str = strlen(buffer);
//Save reversed number data.
reverseString(buffer, size_str);
/* here: copy buffer in a new allocated memory stored in number->data. */
number->data = strdup(buffer);
//Save size.
number->size = size_str;
//Return BIG_DECIMAL struct.
return number;
}
And do not forget to free them correctly:
/*DeAllocation*/
free(op1->data);
free(op1);
free(op2->data);
free(op2);
There are stell some errors in your code: the beginning of BD_addition function should looks like:
BIG_DECIMAL * BD_addition(BIG_DECIMAL *dec1, BIG_DECIMAL *dec2) {
//local variable in this func.
int carry = 0;
BIG_DECIMAL *result = malloc(sizeof(BIG_DECIMAL));
/* compute the size of result */
result->size = (dec1->size < dec2->size) ? dec1->size : dec2->size;
/* take in account an eventual carry */
result->size += 1;
/* allocate */
result->data = malloc(result->size+1);
//Adding loop start
....
And your macro D_C does not seem valid (0 is not converted to '0').
If you like, this comes without struct, strdup, reverse etc. just one malloc.
#include <stdlib.h>
#define toI(x) ((x)-'0')
#define toC(x) ((x)+'0')
#define max(a,b) ((a)>(b)) ? (a):(b)
char *add(char *buf1, char *buf2) {
int size, v1, v2, r, carry=0;
char *ap1, *ep1, *ap2, *ep2, *ap3, *ep3, *rp, *result;
for(ep1=ap1=buf1; *ep1; ep1++);
for(ep2=ap2=buf2; *ep2; ep2++);
size=max(ep2-ap2, ep1-ap1);
ap3=ep3=rp=result=malloc(size+10);
ep3+=size+10;
rp=ep3-1;
*rp='\0';
for(ep1--, ep2--, rp--; ep1>=ap1 || ep2>=ap2; ep1--, ep2--, rp--) {
v1 = ep1>=ap1 ? toI(*ep1) : 0;
v2 = ep2>=ap2 ? toI(*ep2) : 0;
r = v1+v2+carry;
*rp=toC(r%10);
carry=r/10;
}
if(carry!=0) *rp-- = toC(carry);
for(rp++;rp<ep3; rp++, ap3++)
*ap3=*rp;
return result;
}
int main() {
char *result = add("123456789", "12345678");
printf("\n%s\n", result);
free(result);
}

Decimal to Binary conversion not working

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int myatoi(const char* string) {
int i = 0;
while (*string) {
i = (i << 3) + (i<<1) + (*string -'0');
string++;
}
return i;
}
void decimal2binary(char *decimal, int *binary) {
decimal = malloc(sizeof(char) * 32);
long int dec = myatoi(decimal);
long int fraction;
long int remainder;
long int factor = 1;
long int fractionfactor = .1;
long int wholenum;
long int bin;
long int onechecker;
wholenum = (int) dec;
fraction = dec - wholenum;
while (wholenum != 0 ) {
remainder = wholenum % 2; // get remainder
bin = bin + remainder * factor; // store the binary as you get remainder
wholenum /= 2; // divide by 2
factor *= 10; // times by 10 so it goes to the next digit
}
long int binaryfrac = 0;
int i;
for (i = 0; i < 10; i++) {
fraction *= 2; // times by two first
onechecker = fraction; // onechecker is for checking if greater than one
binaryfrac += fractionfactor * onechecker; // store into binary as you go
if (onechecker == 1) {
fraction -= onechecker; // if greater than 1 subtract the 1
}
fractionfactor /= 10;
}
bin += binaryfrac;
*binary = bin;
free(decimal);
}
int main(int argc, char **argv) {
char *data;
data = malloc(sizeof(char) * 32);
int datai = 1;
if (argc != 4) {
printf("invalid number of arguments\n");
return 1;
}
if (strcmp(argv[1], "-d")) {
if (strcmp(argv[3], "-b")) {
decimal2binary(argv[2], &datai);
printf("output is : %d" , datai);
} else {
printf("invalid parameter");
}
} else {
printf("invalid parameter");
}
free(data);
return 0;
}
In this problem, myatoi works fine and the decimal2binary algorithm is correct, but every time I run the code it gives my output as 0. I do not know why. Is it a problem with pointers? I already set the address of variable data but the output still doesn't change.
./dec2bin "-d" "23" "-b"
The line:
long int fractionfactor = .1;
will set fractionfactor to 0 because the variable is defined as an integer. Try using a float or double instead.
Similarly,
long int dec = myatoi(decimal);
stores an integer value, so wholenum is unnecessary.
Instead of
i = (i << 3) + (i<<1) + (*string -'0');
the code will be much more readable as
i = i * 10 + (*string - '0');
and, with today's optimizing compilers, both versions will likely generate the same object code. In general, especially when your code isn't working, favor readability over optimization.
fraction *= 2; // times by two first
Comments like this, that simply translate code to English, are unnecessary unless you're using the language in an unusual way. You can assume the reader is familiar with the language; it's far more helpful to explain your reasoning instead.
Another coding tip: instead of writing
if (strcmp(argv[1], "-d")) {
if (strcmp(argv[3], "-b")) {
decimal2binary(argv[2], &datai);
printf("output is : %d" , datai);
} else {
printf("invalid parameter");
}
} else {
printf("invalid parameter");
}
you can refactor the nested if blocks to make them simpler and easier to understand. In general it's a good idea to check for error conditions early, to separate the error-checking from the core processing, and to explain errors as specifically as possible so the user will know how to correct them.
If you do this, it may also be easier to realize that both of the original conditions should be negated:
if (strcmp(argv[1], "-d") != 0) {
printf("Error: first parameter must be -d\n");
else if (strcmp(argv[3], "-b") != 0) {
printf("Error: third parameter must be -b\n");
} else {
decimal2binary(argv[2], &datai);
printf("Output is: %d\n" , datai);
}
void decimal2binary(char *decimal, int *binary) {
decimal = malloc(sizeof(char) * 32);
...
}
The above lines of code allocate a new block of memory to decimal, which will then no longer point to the input data. Then the line
long int dec = myatoi(decimal);
assigns the (random values in the) newly-allocated memory to dec.
So remove the line
decimal = malloc(sizeof(char) * 32);
and you will get the correct answer.
if(!strcmp(argv[3] , "-b"))
if(!strcmp(argv[3] , "-d"))
The result of the string compare function should be negated so that you can proceed. Else it will print invalid parameter. Because the strcmp returns '0' when the string is equal.
In the 'decimal2binary' function you are allocating a new memory block inside the function for the input parameter 'decimal',
decimal = malloc(sizeof(char) * 32);
This would actually overwrite your input parameter data.

How would you implement the pilloried function in the Daily WTF?

The Daily WTF for 2008-11-28 pillories the following code:
static char *nice_num(long n)
{
int neg = 0, d = 3;
char *buffer = prtbuf;
int bufsize = 20;
if (n < 0)
{
neg = 1;
n = -n;
}
buffer += bufsize;
*--buffer = '\0';
do
{
*--buffer = '0' + (n % 10);
n /= 10;
if (--d == 0)
{
d = 3;
*--buffer = ',';
}
}
while (n);
if (*buffer == ',') ++buffer;
if (neg) *--buffer = '-';
return buffer;
}
How would you write it?
If you're a seasoned C programmer, you'll realize this code isn't actually that bad. It's relatively straightforward (for C), and it's blazingly fast. It has three problems:
It fails on the edge case of LONG_MIN (-2,147,483,648), since negating this number produces itself in twos-complement
It assumes 32-bit integers - for 64-bit longs, a 20-byte buffer is not big enough
It's not thread-safe - it uses a global static buffer, so multiple threads calling it at the same time will result in a race condition
Problem #1 is easily solved with a special case. To address #2, I'd separate the code into two functions, one for 32-bit integers and one for 64-bit integers. #3 is a little harder - we have to change the interface to make completely thread-safe.
Here is my solution, based on this code but modified to address these problems:
static int nice_num(char *buffer, size_t len, int32_t n)
{
int neg = 0, d = 3;
char buf[16];
size_t bufsize = sizeof(buf);
char *pbuf = buf + bufsize;
if(n < 0)
{
if(n == INT32_MIN)
{
strncpy(buffer, "-2,147,483,648", len);
return len <= 14;
}
neg = 1;
n = -n;
}
*--pbuf = '\0';
do
{
*--pbuf = '0' + (n % 10);
n /= 10;
if(--d == 0)
{
d = 3;
*--pbuf = ',';
}
}
while(n > 0);
if(*pbuf == ',') ++pbuf;
if(neg) *--pbuf = '-';
strncpy(buffer, pbuf, len);
return len <= strlen(pbuf);
}
Explanation: it creates a local buffer on the stack and then fills that in in the same method as the initial code. Then, it copies it into a parameter passed into the function, making sure not to overflow the buffer. It also has a special case for INT32_MIN. The return value is 0 if the original buffer was large enough, or 1 if the buffer was too small and the resulting string was truncated.
Hmm... I guess I shouldn't admit this, but my int to string routine for an embedded system work in pretty much exactly the same way (but without putting in the commas).
It's not particularly straightforward, but I wouldn't call it a WTF if you're working on a system that you can't use snprintf() on.
The guy who wrote the above probably noted that the printf() family of routines can't do comma grouping, so he came up with his own.
Footnote: there are some libraries where the printf() style formatting does support grouping, but they are not standard. And I know that the posted code doesn't support other locales that group using '.'. But that's hardly a WTF, just a bug possibly.
That's probably pretty close to the way I would write it actually. The only thing I can immediately see that is wrong with the solution is that is doesn't work for LONG_MIN on machines where LONG_MIN is -(LONG_MAX + 1), which is most machines nowadays. I might use localeconv to get the thousands separator instead of assuming comma, and I might more carefully calculate the buffer size, but the algorithm and implementation seem pretty straight-forward to me, not really much of a WTF for C (there are much better solutions for C++).
Lisp:
(defun pretty-number (x) (format t "~:D" x))
I'm suprised how easily I could do this. I'm not even past the first chapter in my Lisp book. xD (Or should I say, ~:D)
size_t
signed_as_text_grouped_on_powers_of_1000(char *s, ssize_t max, int n)
{
if (max <= 0)
return 0;
size_t r=0;
bool more_groups = n/1000 != 0;
if (more_groups)
{
r = signed_as_text_grouped_on_powers_of_1000(s, max, n/1000);
r += snprintf(s+r, max-r, ",");
n = abs(n%1000);
r += snprintf(s+r, max-r, "%03d",n);
} else
r += snprintf(s+r, max-r, "% 3d", n);
return r;
}
Unfortunately, this is about 10x slower than the original.
In pure C:
#include <stdio.h>
#include <limits.h>
static char *prettyNumber(long num, int base, char separator)
{
#define bufferSize (sizeof(long) * CHAR_BIT)
static char buffer[bufferSize + 1];
unsigned int pos = 0;
/* We're walking backwards because numbers are right to left. */
char *p = buffer + bufferSize;
*p = '\0';
int negative = num < 0;
do
{
char digit = num % base;
digit += '0';
*(--p) = digit;
++pos;
num /= base;
/* This the last of a digit group? */
if(pos % 3 == 0)
{
/* TODO Make this a user setting. */
#ifndef IM_AMERICAN
# define IM_AMERICAN_BOOL 0
#else
# define IM_AMERICAN_BOOL 1
#endif
/* Handle special thousands case. */
if(!IM_AMERICAN_BOOL && pos == 3 && num < base)
{
/* DO NOTHING */
}
else
{
*(--p) = separator;
}
}
} while(num);
if(negative)
*(--p) = '-';
return p;
#undef bufferSize
}
int main(int argc, char **argv)
{
while(argc > 1)
{
long num = 0;
if(sscanf(argv[1], "%ld", &num) != 1)
continue;
printf("%ld = %s\n", num, prettyNumber(num, 10, ' '));
--argc;
++argv;
};
return 0;
}
Normally I'd return an alloc'd buffer, which would need to be free'd by the user. This addition is trivial.
I got bored and made this naive implementation in Perl. Works.
sub pretify {
my $num = $_[0];
my $numstring = sprintf( "%f", $num );
# Split into whole/decimal
my ( $whole, $decimal ) = ( $numstring =~ /(^\d*)(.\d+)?/ );
my #chunks;
my $output = '';
# Pad whole into multiples of 3
$whole = q{ } x ( 3 - ( length $whole ) % 3 ) . $whole;
# Create an array of all 3 parts.
#chunks = $whole =~ /(.{3})/g;
# Reassemble with commas
$output = join ',', #chunks;
if ($decimal) {
$output .= $decimal;
}
# Strip Padding ( and spurious commas )
$output =~ s/^[ ,]+//;
# Strip excess tailing zeros
$output =~ s/0+$//;
# Ending with . is ugly
$output =~ s/\.$//;
return $output;
}
print "\n", pretify 100000000000000000000000000.0000;
print "\n", pretify 10_202_030.45;
print "\n", pretify 10_101;
print "\n", pretify 0;
print "\n", pretify 0.1;
print "\n", pretify 0.0001;
print "\n";

Resources