Avoid trailing zeroes in printf() - c

I keep stumbling on the format specifiers for the printf() family of functions. What I want is to be able to print a double (or float) with a maximum given number of digits after the decimal point. If I use:
printf("%1.3f", 359.01335);
printf("%1.3f", 359.00999);
I get
359.013
359.010
Instead of the desired
359.013
359.01
Can anybody help me?

This can't be done with the normal printf format specifiers. The closest you could get would be:
printf("%.6g", 359.013); // 359.013
printf("%.6g", 359.01); // 359.01
but the ".6" is the total numeric width so
printf("%.6g", 3.01357); // 3.01357
breaks it.
What you can do is to sprintf("%.20g") the number to a string buffer then manipulate the string to only have N characters past the decimal point.
Assuming your number is in the variable num, the following function will remove all but the first N decimals, then strip off the trailing zeros (and decimal point if they were all zeros).
char str[50];
sprintf (str,"%.20g",num); // Make the number.
morphNumericString (str, 3);
: :
void morphNumericString (char *s, int n) {
char *p;
int count;
p = strchr (s,'.'); // Find decimal point, if any.
if (p != NULL) {
count = n; // Adjust for more or less decimals.
while (count >= 0) { // Maximum decimals allowed.
count--;
if (*p == '\0') // If there's less than desired.
break;
p++; // Next character.
}
*p-- = '\0'; // Truncate string.
while (*p == '0') // Remove trailing zeros.
*p-- = '\0';
if (*p == '.') { // If all decimals were zeros, remove ".".
*p = '\0';
}
}
}
If you're not happy with the truncation aspect (which would turn 0.12399 into 0.123 rather than rounding it to 0.124), you can actually use the rounding facilities already provided by printf. You just need to analyse the number before-hand to dynamically create the widths, then use those to turn the number into a string:
#include <stdio.h>
void nDecimals (char *s, double d, int n) {
int sz; double d2;
// Allow for negative.
d2 = (d >= 0) ? d : -d;
sz = (d >= 0) ? 0 : 1;
// Add one for each whole digit (0.xx special case).
if (d2 < 1) sz++;
while (d2 >= 1) { d2 /= 10.0; sz++; }
// Adjust for decimal point and fractionals.
sz += 1 + n;
// Create format string then use it.
sprintf (s, "%*.*f", sz, n, d);
}
int main (void) {
char str[50];
double num[] = { 40, 359.01335, -359.00999,
359.01, 3.01357, 0.111111111, 1.1223344 };
for (int i = 0; i < sizeof(num)/sizeof(*num); i++) {
nDecimals (str, num[i], 3);
printf ("%30.20f -> %s\n", num[i], str);
}
return 0;
}
The whole point of nDecimals() in this case is to correctly work out the field widths, then format the number using a format string based on that. The test harness main() shows this in action:
40.00000000000000000000 -> 40.000
359.01335000000000263753 -> 359.013
-359.00999000000001615263 -> -359.010
359.00999999999999090505 -> 359.010
3.01357000000000008200 -> 3.014
0.11111111099999999852 -> 0.111
1.12233439999999995429 -> 1.122
Once you have the correctly rounded value, you can once again pass that to morphNumericString() to remove trailing zeros by simply changing:
nDecimals (str, num[i], 3);
into:
nDecimals (str, num[i], 3);
morphNumericString (str, 3);
(or calling morphNumericString at the end of nDecimals but, in that case, I'd probably just combine the two into one function), and you end up with:
40.00000000000000000000 -> 40
359.01335000000000263753 -> 359.013
-359.00999000000001615263 -> -359.01
359.00999999999999090505 -> 359.01
3.01357000000000008200 -> 3.014
0.11111111099999999852 -> 0.111
1.12233439999999995429 -> 1.122

To get rid of the trailing zeros, you should use the "%g" format:
float num = 1.33;
printf("%g", num); //output: 1.33
After the question was clarified a bit, that suppressing zeros is not the only thing that was asked, but limiting the output to three decimal places was required as well. I think that can't be done with sprintf format strings alone. As Pax Diablo pointed out, string manipulation would be required.

I like the answer of R. slightly tweaked:
float f = 1234.56789;
printf("%d.%.0f", f, 1000*(f-(int)f));
'1000' determines the precision.
Power to the 0.5 rounding.
EDIT
Ok, this answer was edited a few times and I lost track what I was thinking a few years back (and originally it did not fill all the criteria). So here is a new version (that fills all criteria and handles negative numbers correctly):
double f = 1234.05678900;
char s[100];
int decimals = 10;
sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals));
printf("10 decimals: %d%s\n", (int)f, s+1);
And the test cases:
#import <stdio.h>
#import <stdlib.h>
#import <math.h>
int main(void){
double f = 1234.05678900;
char s[100];
int decimals;
decimals = 10;
sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals));
printf("10 decimals: %d%s\n", (int)f, s+1);
decimals = 3;
sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals));
printf(" 3 decimals: %d%s\n", (int)f, s+1);
f = -f;
decimals = 10;
sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals));
printf(" negative 10: %d%s\n", (int)f, s+1);
decimals = 3;
sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals));
printf(" negative 3: %d%s\n", (int)f, s+1);
decimals = 2;
f = 1.012;
sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals));
printf(" additional : %d%s\n", (int)f, s+1);
return 0;
}
And the output of the tests:
10 decimals: 1234.056789
3 decimals: 1234.057
negative 10: -1234.056789
negative 3: -1234.057
additional : 1.01
Now, all criteria are met:
maximum number of decimals behind the zero is fixed
trailing zeros are removed
it does it mathematically right (right?)
works (now) also when first decimal is zero
Unfortunately this answer is a two-liner as sprintf does not return the string.

Why not just do this?
double f = 359.01335;
printf("%g", round(f * 1000.0) / 1000.0);

I search the string (starting rightmost) for the first character in the range 1 to 9 (ASCII value 49-57) then null (set to 0) each char right of it - see below:
void stripTrailingZeros(void) {
//This finds the index of the rightmost ASCII char[1-9] in array
//All elements to the left of this are nulled (=0)
int i = 20;
unsigned char char1 = 0; //initialised to ensure entry to condition below
while ((char1 > 57) || (char1 < 49)) {
i--;
char1 = sprintfBuffer[i];
}
//null chars left of i
for (int j = i; j < 20; j++) {
sprintfBuffer[i] = 0;
}
}

What about something like this (might have rounding errors and negative-value issues that need debugging, left as an exercise for the reader):
printf("%.0d%.4g\n", (int)f/10, f-((int)f-(int)f%10));
It's slightly programmatic but at least it doesn't make you do any string manipulation.

Some of the highly voted solutions suggest the %g conversion specifier of printf. This is wrong because there are cases where %g will produce scientific notation. Other solutions use math to print the desired number of decimal digits.
I think the easiest solution is to use sprintf with the %f conversion specifier and to manually remove trailing zeros and possibly a decimal point from the result. Here's a C99 solution:
#include <stdio.h>
#include <stdlib.h>
char*
format_double(double d) {
int size = snprintf(NULL, 0, "%.3f", d);
char *str = malloc(size + 1);
snprintf(str, size + 1, "%.3f", d);
for (int i = size - 1, end = size; i >= 0; i--) {
if (str[i] == '0') {
if (end == i + 1) {
end = i;
}
}
else if (str[i] == '.') {
if (end == i + 1) {
end = i;
}
str[end] = '\0';
break;
}
}
return str;
}
Note that the characters used for digits and the decimal separator depend on the current locale. The code above assumes a C or US English locale.

A simple solution but it gets the job done, assigns a known length and precision and avoids the chance of going exponential format (which is a risk when you use %g):
// Since we are only interested in 3 decimal places, this function
// can avoid any potential miniscule floating point differences
// which can return false when using "=="
int DoubleEquals(double i, double j)
{
return (fabs(i - j) < 0.000001);
}
void PrintMaxThreeDecimal(double d)
{
if (DoubleEquals(d, floor(d)))
printf("%.0f", d);
else if (DoubleEquals(d * 10, floor(d * 10)))
printf("%.1f", d);
else if (DoubleEquals(d * 100, floor(d* 100)))
printf("%.2f", d);
else
printf("%.3f", d);
}
Add or remove "elses" if you want a max of 2 decimals; 4 decimals; etc.
For example if you wanted 2 decimals:
void PrintMaxTwoDecimal(double d)
{
if (DoubleEquals(d, floor(d)))
printf("%.0f", d);
else if (DoubleEquals(d * 10, floor(d * 10)))
printf("%.1f", d);
else
printf("%.2f", d);
}
If you want to specify the minimum width to keep fields aligned, increment as necessary, for example:
void PrintAlignedMaxThreeDecimal(double d)
{
if (DoubleEquals(d, floor(d)))
printf("%7.0f", d);
else if (DoubleEquals(d * 10, floor(d * 10)))
printf("%9.1f", d);
else if (DoubleEquals(d * 100, floor(d* 100)))
printf("%10.2f", d);
else
printf("%11.3f", d);
}
You could also convert that to a function where you pass the desired width of the field:
void PrintAlignedWidthMaxThreeDecimal(int w, double d)
{
if (DoubleEquals(d, floor(d)))
printf("%*.0f", w-4, d);
else if (DoubleEquals(d * 10, floor(d * 10)))
printf("%*.1f", w-2, d);
else if (DoubleEquals(d * 100, floor(d* 100)))
printf("%*.2f", w-1, d);
else
printf("%*.3f", w, d);
}

I found problems in some of the solutions posted. I put this together based on answers above. It seems to work for me.
int doubleEquals(double i, double j) {
return (fabs(i - j) < 0.000001);
}
void printTruncatedDouble(double dd, int max_len) {
char str[50];
int match = 0;
for ( int ii = 0; ii < max_len; ii++ ) {
if (doubleEquals(dd * pow(10,ii), floor(dd * pow(10,ii)))) {
sprintf (str,"%f", round(dd*pow(10,ii))/pow(10,ii));
match = 1;
break;
}
}
if ( match != 1 ) {
sprintf (str,"%f", round(dd*pow(10,max_len))/pow(10,max_len));
}
char *pp;
int count;
pp = strchr (str,'.');
if (pp != NULL) {
count = max_len;
while (count >= 0) {
count--;
if (*pp == '\0')
break;
pp++;
}
*pp-- = '\0';
while (*pp == '0')
*pp-- = '\0';
if (*pp == '.') {
*pp = '\0';
}
}
printf ("%s\n", str);
}
int main(int argc, char **argv)
{
printTruncatedDouble( -1.999, 2 ); // prints -2
printTruncatedDouble( -1.006, 2 ); // prints -1.01
printTruncatedDouble( -1.005, 2 ); // prints -1
printf("\n");
printTruncatedDouble( 1.005, 2 ); // prints 1 (should be 1.01?)
printTruncatedDouble( 1.006, 2 ); // prints 1.01
printTruncatedDouble( 1.999, 2 ); // prints 2
printf("\n");
printTruncatedDouble( -1.999, 3 ); // prints -1.999
printTruncatedDouble( -1.001, 3 ); // prints -1.001
printTruncatedDouble( -1.0005, 3 ); // prints -1.001 (shound be -1?)
printTruncatedDouble( -1.0004, 3 ); // prints -1
printf("\n");
printTruncatedDouble( 1.0004, 3 ); // prints 1
printTruncatedDouble( 1.0005, 3 ); // prints 1.001
printTruncatedDouble( 1.001, 3 ); // prints 1.001
printTruncatedDouble( 1.999, 3 ); // prints 1.999
printf("\n");
exit(0);
}

Here is my first try at an answer:
void
xprintfloat(char *format, float f)
{
char s[50];
char *p;
sprintf(s, format, f);
for(p=s; *p; ++p)
if('.' == *p) {
while(*++p);
while('0'==*--p) *p = '\0';
}
printf("%s", s);
}
Known bugs: Possible buffer overflow depending on format. If "." is present for other reason than %f wrong result might happen.

Slight variation on above:
Eliminates period for case (10000.0).
Breaks after first period is processed.
Code here:
void EliminateTrailingFloatZeros(char *iValue)
{
char *p = 0;
for(p=iValue; *p; ++p) {
if('.' == *p) {
while(*++p);
while('0'==*--p) *p = '\0';
if(*p == '.') *p = '\0';
break;
}
}
}
It still has potential for overflow, so be careful ;P

I would say you should use
printf("%.8g",value);
If you use "%.6g" you will not get desired output for some numbers like.32.230210 it should print 32.23021 but it prints 32.2302

Hit the same issue, double precision is 15 decimal, and float precision is 6 decimal, so I wrote to 2 functions for them separately
#include <stdio.h>
#include <math.h>
#include <string>
#include <string.h>
std::string doublecompactstring(double d)
{
char buf[128] = {0};
if (isnan(d))
return "NAN";
sprintf(buf, "%.15f", d);
// try to remove the trailing zeros
size_t ccLen = strlen(buf);
for(int i=(int)(ccLen -1);i>=0;i--)
{
if (buf[i] == '0')
buf[i] = '\0';
else
break;
}
return buf;
}
std::string floatcompactstring(float d)
{
char buf[128] = {0};
if (isnan(d))
return "NAN";
sprintf(buf, "%.6f", d);
// try to remove the trailing zeros
size_t ccLen = strlen(buf);
for(int i=(int)(ccLen -1);i>=0;i--)
{
if (buf[i] == '0')
buf[i] = '\0';
else
break;
}
return buf;
}
int main(int argc, const char* argv[])
{
double a = 0.000000000000001;
float b = 0.000001f;
printf("a: %s\n", doublecompactstring(a).c_str());
printf("b: %s\n", floatcompactstring(b).c_str());
return 0;
}
output is
a: 0.000000000000001
b: 0.000001

I needed that and the first answer from paxdiablo does the trick. But I was not needing truncating and the version below is maybe slightly faster?
Starting to search end of string (EOS) after the ".", only one placement of EOS.
//https://stackoverflow.com/questions/277772/avoid-trailing-zeroes-in-printf
//adapted from paxdiablo (removed truncating)
char StringForDouble[50];
char *PointerInString;
void PrintDouble (double number) {
sprintf(StringForDouble,"%.10f",number); // convert number to string
PointerInString=strchr(&StringForDouble[0],'.'); // find decimal point, if any
if(PointerInString!=NULL) {
PointerInString=strchr(&PointerInString[0],'\0'); // find end of string
do{
PointerInString--;
} while(PointerInString[0]=='0'); // remove trailing zeros
if (PointerInString[0]=='.') { // if all decimals were zeros, remove "."
PointerInString[0]='\0';
} else {
PointerInString[1]='\0'; //otherwise put EOS after the first non zero char
}
}
printf("%s",&StringForDouble[0]);
}

My idea is to calculate the required precision that would not result in trailing zeroes for a given double value and pass it to the "%1.*f" format in printf().
This can even be done as one-liner:
int main() {
double r=1234.56789;
int precision=3;
printf(L"%1.*f", prec(r, precision), r);
}
int prec(const double& r, int precision)
{
double rPos = (r < 0)? -r : r;
double nkd = fmod(rPos, 1.0); // 0..0.99999999
int i, ex10 = 1;
for (i = 0; i < precision; ++i)
ex10 *= 10;
int nki = (int)(nkd * ex10 + 0.5);
// "Eliminate" trailing zeroes
int requiredPrecision = precision;
for (; requiredPrecision && !(nki % 10); ) {
--requiredPrecision;
nki /= 10;
}
return requiredPrecision;
}
And here is another %g solution. You should always provide a format precision that is "wide enough" (default is only 6) and round the value. I think this is a nice way to do it:
double round(const double &value, const double& rounding) {
return rounding!=0 ? floor(value/rounding + 0.5)*rounding : value;
}
printf("%.12g" round(val, 0.001)); // prints up to 3 relevant digits

Your code rounds to three decimal places due to the ".3" before the f
printf("%1.3f", 359.01335);
printf("%1.3f", 359.00999);
Thus if you the second line rounded to two decimal places, you should change it to this:
printf("%1.3f", 359.01335);
printf("%1.2f", 359.00999);
That code will output your desired results:
359.013
359.01
*Note this is assuming you already have it printing on separate lines, if not then the following will prevent it from printing on the same line:
printf("%1.3f\n", 359.01335);
printf("%1.2f\n", 359.00999);
The Following program source code was my test for this answer
#include <cstdio>
int main()
{
printf("%1.3f\n", 359.01335);
printf("%1.2f\n", 359.00999);
while (true){}
return 0;
}

Related

Adding 2 binary strings

I'm passing almost all leetCode tests with this, but not understanding why the output is wrong ("/0") when the input is:
a = "10100000100100110110010000010101111011011001101110111111111101000000101111001110001111100001101"
b = "110101001011101110001111100110001010100001101011101010000011011011001011101111001100000011011110011"
Anyone has an idea to what is not working ?
Thanks
#include <stdio.h>
#include <stdlib.h>
char * sumBinary(long int binary1, long int binary2, char * result);
char * addBinary(char * a, char * b)
{
char * result;
long int a_int;
long int b_int;
a_int = atoi(a);
b_int = atoi(b);
result = malloc(sizeof(*result) * 1000);
if (!result)
return (NULL);
sumBinary(a_int, b_int, result);
return (result);
}
char * sumBinary(long int binary1, long int binary2, char * result)
{
int i;
int t;
int rem;
int sum[1000];
i = 0;
t = 0;
rem = 0;
if ((binary1 == 0) && (binary2 == 0))
{
result[0] = '0';
result[1] = '\0';
}
else
{
while (binary1 != 0 || binary2 != 0)
{
sum[i++] = (binary1 %10 + binary2 % 10 + rem) % 2;
rem = (binary1 %10 + binary2 % 10 + rem) / 2;
binary1 = binary1 / 10;
binary2 = binary2 / 10;
}
if (rem != 0)
sum[i++] = rem;
--i;
while (i >= 0)
{
result[t] = sum[i] + '0';
t++;
i--;
}
result[t] = '\0';
}
return (result);
}
For a start, you should be using atol(3), not atoi(3) if you're using long int. But that's not the main issue here.
atol(3) and atoi(3) expect strings containing decimal numbers, not binary, so that's not going to work well for you. You would need strtol(3), which you can tell to expect a string in ASCII binary. But again, this is not the main issue.
You don't give the question text, but I'm guessing they want you to add two arbitrarily-long ASCII-binary strings, resulting in an ASCII-binary string.
I imagine their expectation, given it's arbitrarily-long, is that you would be working entirely in the string domain. So you'd allocate for a string whose length is two greater than the longer of the two you get as parameters (+1 for the terminal NUL, the other +1 for a potential overflow digit).
Then you start from the end, working back to the start, adding the corresponding digits of the parameter strings, placing the results into the result string starting from its end (allowing for that terminal NUL), adding as if you were doing it by hand.
Don't forget to add a leading zero to the result string, if you don't overflow into that position.
Note that I'm not going to write the code for you. This is either a learning exercise or a test: either way, you need to do the coding so you can learn from it.

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);
}

printf double with maximum precision in available width

I need to print doubles in C, with a width of 20, but using the maximum precision that can be printed in the 20 chars
For example:
-123.123456789123456789
should be printed:
-123.1234567891234568
Up to now I have the following code that works, but is ugly and has a problem:
double num = .......;
int pr = 0;
char temp[32] = { 0 };
char *point = NULL;
sprintf(temp, "%.20f", num);
point = strchr(temp, '.');
if (point) {
pr = 20 - (1 + point - temp);
}
printf("%20.*f", pr, num);
The problem is that this does not work with %g instead of %f, it prints more than 20 characters sometimes, even though no exponential part exists.
The problem arises when a number like 0.000123456789123456789 is given. The extra zeroes after the decimal do not seem to count (!).
Following some hints from chux and this answer, I implemented the following solution that seems to work.
I no longer try to pre-calculate the correct precision to use, but print with more than enough precision and later I crop the superfluous digits.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void printDouble(double val, int width)
{
char *temp = malloc((width + 32) * sizeof(char));
int len = 0;
sprintf(temp, "%*.*g", width, width, val);
len =strlen(temp);
if (len > width) {
char *dropped = temp + width;
char *last = dropped - 1;
char *expon = strchr(temp, 'E');
if (!expon)
expon = strchr(temp, 'e');
if (expon) {
last = temp + width - 1 - strlen(expon);
dropped = last + 1 < expon ? last + 1 : NULL;
}
if (strchr(temp, '.')) {
/* Round */
if (dropped && *dropped >= '5' && *dropped <= '9') {
while (*last == '9')
last--;
(*last)++;
}
/* Remove trailing zeroes */
while (*last == '0')
last--;
}
if (expon) {
while (*expon != '\0')
*(++last) = *(expon++);
}
*(++last) = '\0';
}
printf("%*s\n", width, temp);
free(temp);
}

Converting character array to Floating Point with 2 Decimal Places

I have defined a character array like this.
char pkgamount[20]="";
This can be populated by values like 120, -120, -120.23 etc (Up to 2 decimal places)
I currently convert the char array to integer like this.
int amt2;
sscanf(pkgamount, "%d", &amt2);
What is the proper way to convert the character array that can containing negative decimals to a float value with only 2 decimal places?
UPDATE: #1
I read values from a database (VARCHAR) to the character array.These value will have a maximum of 2 decimal places.The value can be 0/With 2 decimal places/with no decimal places at all. I think integer data type cannot have decimal places. I need to add/subtract these values.So I need to convert the character array to float.
UPDATE #2
I tried using float and I keep getting some random output.Please the extracted code below
float bamt1,bamt2,amt1,amt2;
float balance1,balance2;
sscanf(stbamount, "%f", &amt1);
sscanf(pkgamount, "%f", &amt2);
//amoun1=32 amount2=23 any decimal or non decimal values..using API
//Using API on device to read this..skipping the code for that
sscanf(amount1, "%f", &bamt1);
sscanf(amount2, "%f", &bamt2);
balance1=amt1-bamt1;
balance2=amt2-bamt2;
// buffers used for conversion and printing
char buf1[sizeof(int)*3+2]="";
snprintf(buf1, sizeof buf1, "%f", balance1);
char buf2[sizeof(int)*3+2]="";
char sbal[20]="";
char pbal[20]="";
strcat(sbal,"STB Balance: ");
strcat(pbal,"PKG Balance: ");
strcat(sbal,buf1);
strcat(pbal,buf2);
//prn_write_text is API Call to run thermal printer on device
prn_write_text(sbal,strlen(sbal),1);
prn_write_text(pbal,strlen(pbal),1);
snprintf(buf2, sizeof buf2, "%f", balance2);
UPDATE #3
This is the API Call to use the thermal printer to write some text. I need to convert the floating point value to a string and cut it, so that it includes only 2 decimal values.
Using floats to store the numbers..
float amtf;
sscanf(pkgamount, "%f", &amtf);
To print the numbers to 2 decimal places
printf (".2%f",amtf);
You can use floats to store the numbers as long as you are willing to tolerate errors of possibly 0.01 during addition or subtraction. It makes the code easier.
However, if you absolutely have to be accurate down to the last 0.00 then you should use integers * 100 to store the data, as suggested by M Oehem.
Just loop through your characters
enum parseFloatErrorCodes {
SUCCESS, DOUBLE_DOT,PARSE_ERROR, INTEGER_OVERFLOW,
TOO_MANY_DIGITS_AFTER_DECIMAL_POINT
};
int parse_float(char* inp, float *x) {
char *p;
int ival, dot;
for (ival = 0, dot = 0, p = inp; *p; ++p) {
int overflow_check = ival;
if (('0' <= *p) && (*p <= '9')) {
ival = ival * 10 + (int)(*p - '0');
if (dot > 0) ++dot;
} else if (*p == '.') {
if (dot > 0) { return DOUBLE_DOT; }
dot = 1;
} else { return PARSE_ERROR; }
if (overflow_check > ival) { return INTEGER_OVERFLOW; }
}
*x = ival;
if (dot == 0) *x *= 100.0;
else if (dot == 1) *x *= 100.0;
else if (dot == 2) *x *= 10.0;
else if (dot > 3) { return TOO_MANY_DIGITS_AFTER_DECIMAL_POINT; }
*x /= 100.0;
return SUCCESS;
}
See the gist for test code that works.
UPDATE #2 writes outside the the character array. --> UB
Assume sizeof(int)== 4 or 8.
// buf1[14] or buf1[26]
char buf1[sizeof(int)*3+2]="";
// the string length of buf1 is at least 8 and likely 10+
snprintf(buf1, sizeof buf1, "%f", balance1);
char sbal[20]="";
strcat(sbal,"STB Balance: "); // 14 char used
// attempts to write at least 14+8 = 22 char, sbal too small.
strcat(sbal,buf1);
Perhaps OP wants
#include <float.h>
// - Digits in FLT_MAX .xx \0
#define FLT_SIZE_2_DP (1 + (FLT_MAX_10_EXP + 1) + (1 + 2) + 1
char sbal[sizeof("STB Balance: ") + FLT_SIZE_2_DP];
int len = snprintf(sbal, sizeof sbal, "STB Balance: %.2f", balance1);
if (len >= 0 && len < sizeof sbal) {
prn_write_text(sbal,strlen(sbal),1);
else {
prn_write_text("Error",5,1);
}
What is the proper way to convert the character array that can containing negative decimals to a float value with only 2 decimal places?
When code has a special input require, useful to create a helper function to meet the need. The below reads 2 integers and then combines them into a float.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
// EOF: end-of-file or IO error
// 0: no conversion
// 1: success
int read_float_2dp(FILE *istream, float *f) {
intmax_t ipart;
char fpart_str[3];
int count = fscanf(istream, "%jd.%2[0-9]", &ipart, fpart_str);
if (count <= 0) return count;
int fpart = (count == 2) ? atoi(fpart_str) : 0;
if (ipart < 0) fpart = -fpart;
*f = ipart + fpart / 100.0f;
return 1;
}
char pkgamount[10],pkgamount1[10];
char something[][3] = {"1.3","53","123","569"};
int amt2;
float amt1;
sscanf(pkgamount,"%2d",&amt2);
sscanf(pkgamount1,"%2.2f",&am1);
the %2d will only scan upto decimal values, same with the float 2 before decimal point and 2 after...Now the problem is i dont know exactly what yu are expecting, thats the reason i defined the something[][3] 2D array, cause thats how yu store values which means something[0][3] something[1][3] something[2][3] so on and so forth that in memory means that array something in place 0 can hold 3 chars, on place 1 can hold 3.....Now its upto yu how exactly yu use a loop to take in values... I can give yu an example..
char something[10][3];
int i;
for (1..10)
{
scanf("%2s",&something[i]);
}
printf("\nSomething array");
for (i..10)
{
printf("\n%2s",something[i]);
}

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.

Resources