Установите прецизионные и зацепляющие нули, но никогда не печатайте экспоненту

Мне нужно:

  • Установите точность так, чтобы поплавки округлялись до сотых (0.111 гравюр как 0,11).
  • Закреплять нулевые нули (1.0 печатает как 1)
  • Никогда не печатайте экспонента (1000,1 печатает как 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

Есть ли опция printf или cout, которая позволит мне обрабатывать все эти данные?

Ответ 1

К сожалению, нет способа заставить потоки использовать поведение printf %f. Таким образом, единственный способ справиться с этим - обрезать десятичные разряды вручную по мере необходимости. Я добавил пример кода С++, который обрабатывает это:

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

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

fullfloat теперь будет содержать правильную строку, но поскольку размер будет продлен после применяемого символа '\0', единственным способом получить его для свойства печати будет использование c_str():

cout << fullfloat.c_str();

Ответ 2

В стандарте C11 говорится о %f и %f (7.21.6.1:8):

Двойной аргумент, представляющий число с плавающей запятой, преобразуется в десятичную нотацию в стиле [-] ddd.ddd, где число цифр после десятичного символа равно спецификации точности. Если точность отсутствует, она принимается равной 6; если точность равна нулю, а флаг # не указан, символ десятичной точки не отображается. Если появляется символ десятичной точки, перед ним появляется как минимум одна цифра. Значение округляется до соответствующего количества цифр.

Вот фрагмент C, который создает то, что вы хотите в блоке malloc ed t, который вам нужно освободить после. Если вы пишете на C99, можно также рассмотреть массив переменной длины.

В приведенном ниже коде не добавлено никакого дополнительного приближения к преобразованию (если ваш printf печатает правильно округленное преобразование в десятичное, как и код ниже) и работает для всех значений с плавающей запятой.

#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

Мне неизвестно какой-либо спецификатор формата, который будет делать то, что вы ищете.

Предварительная переваривание значений перед передачей их отдельным спецификаторам формата может работать. Например:

  • Умножьте исходное число с плавающей запятой на 100 и округлите до ближайшего целого числа
  • Назначить nScaled (int).
  • Назначить mod (nScaled, 100) другому целу, nFractional.
  • Назначить nScaled/100 другому целому числу, nWhole.

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

Вероятно, вы уже знаете это.

Ответ 4

Я думал, что другой вариант - использовать функцию asprintf(): она динамически выделяет строку собственной длины сама по себе. После сохранения строки завершающие нули/точки могут быть обрезаны:

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