Set Precision and Clip Trailing Zeros but Never Print Exponent

余生颓废 提交于 2019-12-29 01:26:07

问题


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?


回答1:


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 [−]ddd.ddd, 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';
}



回答2:


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.




回答3:


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



回答4:


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


来源:https://stackoverflow.com/questions/25403713/set-precision-and-clip-trailing-zeros-but-never-print-exponent

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!