Set Precision and Clip Trailing Zeros but Never Pr

2019-01-20 02:05发布

I need to:

  1. Set precision so that floats are rounded to the hundredths place ( 0.111 prints as 0.11 )
  2. Clip trailing zeros ( 1.0 prints as 1 )
  3. Never print an exponent ( 1000.1 prints as 1000.1 )

printf( "%.2f\n", input ); // handles 1 and 3 but not 2
printf( "%.2g\n", input ); // handles 1 and 2 but not 3
cout << setprecision( 2 ) << input << endl; // handles 1 and 2 but not 3

Is there a printf or cout option that will let me handle all of these?

4条回答
smile是对你的礼貌
2楼-- · 2019-01-20 02:11

The C11 standard says of %f and %F (7.21.6.1:8):

A double argument representing a floating-point number is converted to decimal notation in the style [−]ffffd.ffffd, where the number of digits after the decimal-point character is equal to the precision specification. If the precision is missing, it is taken as 6; if the precision is zero and the # flag is not specified, no decimal-point character appears. If a decimal-point character appears, at least one digit appears before it. The value is rounded to the appropriate number of digits.

Here is a C snippet that produces what you want in a malloced bloc t, that you need to free up afterwards. If you are writing in C99, a variable-length array can also be considered.

The code below does not introduce any additional approximation to the conversion (if your printf prints the correctly rounded conversion to decimal, so will the code below), and works for all floating-point values.

#include <stdio.h>
#include <stdlib.h>
…
int len = snprintf(0, 0, "%.2f", input);
if (len < 0) fail();
char *t = malloc((size_t)len + 1);
if (!t) fail();
if (sprintf(t, "%.2f", input) < 0) fail();
len--;
if (t[len] == '0') {
  len--;
  if (t[len] == '0') {
    len--;
    if (t[len] == '.') len--;
  }
  t[len + 1] = '\0';
}
查看更多
一夜七次
3楼-- · 2019-01-20 02:13

I am not aware of any format specifier that will do what you are looking for.

Pre-digesting the values before passing them to separate format specifiers might work. For example:

  • Multiply the original floating point number by 100 and round to the nearest integer
  • Assign to nScaled (int).
  • Assign mod(nScaled,100) to another integer, nFractional.
  • Assign nScaled/100 to another integer, nWhole.

if( nFractional > 0 )
  printf("%d.%d", nWhole, nFractional );
else
  printf("%d", nWhole );

You probably already know this.

查看更多
叼着烟拽天下
4楼-- · 2019-01-20 02:13

I thought another option is to use asprintf() function: it dynamically allocate a string of proper length by itself. Once the string is stored trailing zeros/dot could be cut off:

...
float num;
...

char *digits;
int i=asprintf(&digits, "%.2f", num)-1;

for(; digits[i] !='.'; i--)
  if (digits[i] == '0') digits[i] = NULL; else break;

if (digits[i] == '.') digits[i] = NULL;

printf("%s\n", digits);
free(digits);
...
查看更多
倾城 Initia
5楼-- · 2019-01-20 02:18

Sadly there is no way to force streams to use printf's %f behavior. So the only way to handle this is trimming decimal places manually as necessary. I've added a C++ code sample that handles this:

string fullfloat(static_cast<size_t>(log10(numeric_limits<declval(input)>::max())) + 5U, '\0'); // Adding 1 for the 10s place, 1 for the '.' 2 for the decimal places, and 1 for the null
const size_t length = size(fullfloat) - sprintf(data(fullfloat), "%.2f", input );

*(--mismatch(rbegin(fullfloat) + length, next(rbegin(fullfloat), 3U + length), "00.").first) = '\0';

fullfloat will now contain the correct string, but because it's size will extend past the applied '\0' character the only way to get it to print property would be to use data():

cout << data(fullfloat);
查看更多
登录 后发表回答