squirl
squirl

Reputation: 1784

Output base-10 fixed point number in C

I have an integer n representing a number multiplied by 1e<exp> for some constant <exp>. For example, for an input number of 12.345 and an <exp> of 4, n = 123450. I want to output the original number, with no trailing zeros, as fast as possible.

Some potential (but flawed) solutions:

// Pros:
//  - Very simple
//  - Does not output trailing zeros
// Cons:
//  - Converts to float (slow, inaccurate)
printf("%g", n * 1e-<exp>);
// Pros:
//  - Quite simple
//  - Uses only integer arithmetic
// Cons:
//  - Outputs trailing zeros
printf("%d.%.0<exp>d", n / (int)1e<exp>, n % (int)1e<exp>);

Upvotes: 1

Views: 550

Answers (2)

chqrlie
chqrlie

Reputation: 145277

Your first naive solution does not work for values of <exp> greater than 6. Here is a simple function. You can adjust the integer type and printf format accordingly:

#include <stdio.h>

int print_fixed(int n, int exp, FILE *fp) {
    char buf[32];
    int len = snprintf(buf + 1, sizeof(buf) - 1, "%.*d", exp + 1, n);
    int i;
    for (i = 0; i < len - exp; i++) {
        buf[i] = buf[i + 1];
    }
    buf[i] = '.';
    while (buf[len] == '0') {
        buf[len--] = '\0';
    }
    if (buf[len] == '.')
        buf[len--] = '\0';
    fputs(buf, fp);
    return len + 1;
}

int main() {
    int tests[][2] = {
        { 0, 0 }, { 1, 0 }, { 10, 1 }, { 100, 2 }, { 1000, 3 }, { 10000, 4 },
        { 1, 0 }, { 1, 1 }, { 1, 2 }, { 1, 3 }, { 1, 4 },
        { 1, 0 }, { 12, 1 }, { 123, 2 }, { 1234, 3 }, { 12345, 4 },
        { 1, 0 }, { 12, 1 }, { 120, 2 }, { 1204, 3 }, { 12040, 4 },
        { -1, 0 }, { -12, 1 }, { -120, 2 }, { -1204, 3 }, { -12040, 4 },
    };
    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        printf("print_fixed(%d, %d, stdout) -> ", tests[i][0], tests[i][1]);
        print_fixed(tests[i][0], tests[i][1], stdout);
        printf("\n");
    }
    return 0;
}

Upvotes: 2

4386427
4386427

Reputation: 44339

I'm not sure how this will perform but you can try it out and let us know.

#define BUF_SZ 32

void p(unsigned n, unsigned exp)
{
    char br[BUF_SZ];
    int ix = BUF_SZ - 1;
    br[ix--] = '\0';
    while(exp)
    {
        // Skip trailing zeros
        int t = n % 10;
        n = n / 10;
        exp--;
        if (t)
        {
            br[ix--] = '0' + t;
            break;
        }
    }

    while(exp)
    {
        br[ix--] = '0' + n % 10;
        n = n / 10;
        exp--;
    }

    // Only insert a '.' if something has been printed
    if (ix != (BUF_SZ - 2)) br[ix--] = '.';

    do
    {
        br[ix--] = '0' + n % 10;
        n = n / 10;
    } while(n);

    puts(&br[ix + 1]);
}

It prints no trailing zero and no '.' when there is no decimals.

Performance unknown.

Upvotes: 1

Related Questions