aecmm
aecmm

Reputation: 21

snprintf floating with specified width and specified padded digits and specified precision

I have a float f = 1.23456. I would like to format it in a string to look like:

M    01.23
M  01.2345

The space depends on variable precision.

My current code is:

snprintf(buf, 10, "M%02.*f", precision, f);  

but the output is not right-justified -> |M01.234 |

Is there a way to get what I want?

Upvotes: 2

Views: 1251

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 754480

You'll need to specify the total digits too, via another *. You also need a bigger buffer — a buffer of 10 bytes isn't big enough to hold a string of length 10. Also, the value printed to 4 d.p. will be 1.2346, not 1.2345.

#include <stdio.h>

int main(void)
{
    double f = 1.23456;
    for (int precision = 2; precision < 6; precision += 2)
    {
        char buffer[32];
        snprintf(buffer, sizeof(buffer), "M%*s%0*.*f", 9 - (precision+3), "", precision+3, precision, f);
        printf("|%s|\n", buffer);
    }
    return 0;
}

Output:

|M    01.23|
|M  01.2346|

The 9 - (precision+3) is a bit delicate; if precision is more than 6, you don't necessarily get the result you want — or if the value is outside the range [0.0 .. 10.0), etc. The +3 (repeated) is also a bit delicate; if the value printed is 10.00 or larger, that needs to change to 4 to get the leading 0 (but do you want 010.2346 or 10.2346?). A precision of zero will need special attention too. What you're asking for is a fiddly format to produce because it isn't a standard one. What should happen with larger numbers, or larger precisions, or smaller numbers, or negative numbers? It isn't clear. This does the requisite job for the two examples for precision and one example value, and shows how variations could be tackled, but it isn't a completely general solution. You might need to determine log10(f) to know what to use in place of the 3, for example.

After clarification in the comments

In summary, the requirements are:

  • The values are in the range [0.00 .. 100.00) (including 0.00, excluding 100.00).
  • There should be two digits printed before the decimal point, with one or two leading zeros as necessary.
  • There should be 1-6 digits printed after the decimal point, depending on the value of precision
  • The number should be right justified in a field of 9 characters, with spaces as necessary before the 2 digits before the decimal point
  • The number (and any leading spaces) should be preceded by the letter M.

This code demonstrates reasonably comprehensively that the original solution could do the job, but it uses more variables so that it is easier to see what's going on.

#include <stdio.h>

int main(void)
{
    enum { WIDTH = 9 };
    for (double f = 1.2345678E-7; f < 100.0; f *= 10.0)
    {
        for (int precision = 1; precision <= 6; precision++)
        {
            char buffer[32];
            int f_width = precision + 3;    // decimal point and 2 digits before
            int blanks = WIDTH - f_width;
            snprintf(buffer, sizeof(buffer), "M%*s%0*.*f", blanks, "", f_width, precision, f);
            printf("|%s| (b = %d; w = %d; p = %d)\n", buffer, blanks, f_width, precision);
            //printf("|%s|\n", buffer);
        }
    }
    return 0;
}

Output:

|M     00.0| (b = 5; w = 4; p = 1)
|M    00.00| (b = 4; w = 5; p = 2)
|M   00.000| (b = 3; w = 6; p = 3)
|M  00.0000| (b = 2; w = 7; p = 4)
|M 00.00000| (b = 1; w = 8; p = 5)
|M00.000000| (b = 0; w = 9; p = 6)
|M     00.0| (b = 5; w = 4; p = 1)
|M    00.00| (b = 4; w = 5; p = 2)
|M   00.000| (b = 3; w = 6; p = 3)
|M  00.0000| (b = 2; w = 7; p = 4)
|M 00.00000| (b = 1; w = 8; p = 5)
|M00.000001| (b = 0; w = 9; p = 6)
|M     00.0| (b = 5; w = 4; p = 1)
|M    00.00| (b = 4; w = 5; p = 2)
|M   00.000| (b = 3; w = 6; p = 3)
|M  00.0000| (b = 2; w = 7; p = 4)
|M 00.00001| (b = 1; w = 8; p = 5)
|M00.000012| (b = 0; w = 9; p = 6)
|M     00.0| (b = 5; w = 4; p = 1)
|M    00.00| (b = 4; w = 5; p = 2)
|M   00.000| (b = 3; w = 6; p = 3)
|M  00.0001| (b = 2; w = 7; p = 4)
|M 00.00012| (b = 1; w = 8; p = 5)
|M00.000123| (b = 0; w = 9; p = 6)
|M     00.0| (b = 5; w = 4; p = 1)
|M    00.00| (b = 4; w = 5; p = 2)
|M   00.001| (b = 3; w = 6; p = 3)
|M  00.0012| (b = 2; w = 7; p = 4)
|M 00.00123| (b = 1; w = 8; p = 5)
|M00.001235| (b = 0; w = 9; p = 6)
|M     00.0| (b = 5; w = 4; p = 1)
|M    00.01| (b = 4; w = 5; p = 2)
|M   00.012| (b = 3; w = 6; p = 3)
|M  00.0123| (b = 2; w = 7; p = 4)
|M 00.01235| (b = 1; w = 8; p = 5)
|M00.012346| (b = 0; w = 9; p = 6)
|M     00.1| (b = 5; w = 4; p = 1)
|M    00.12| (b = 4; w = 5; p = 2)
|M   00.123| (b = 3; w = 6; p = 3)
|M  00.1235| (b = 2; w = 7; p = 4)
|M 00.12346| (b = 1; w = 8; p = 5)
|M00.123457| (b = 0; w = 9; p = 6)
|M     01.2| (b = 5; w = 4; p = 1)
|M    01.23| (b = 4; w = 5; p = 2)
|M   01.235| (b = 3; w = 6; p = 3)
|M  01.2346| (b = 2; w = 7; p = 4)
|M 01.23457| (b = 1; w = 8; p = 5)
|M01.234568| (b = 0; w = 9; p = 6)
|M     12.3| (b = 5; w = 4; p = 1)
|M    12.35| (b = 4; w = 5; p = 2)
|M   12.346| (b = 3; w = 6; p = 3)
|M  12.3457| (b = 2; w = 7; p = 4)
|M 12.34568| (b = 1; w = 8; p = 5)
|M12.345678| (b = 0; w = 9; p = 6)

Upvotes: 3

Related Questions