Могу ли я напечатать -0.000 как 0.000?

printf("%.3lf\n", -0.0001);

Это выводит -0.000, но разве это не должно быть 0.000?

Как я могу сделать этот отпечаток без знака минус, то есть 0.000 ?


person Md Tahsin Rahman    schedule 21.08.2015    source источник
comment
-0.000 является правильным выводом для отрицательного нуля. То, о чем вы просите, - это способ печатать отрицательный ноль, как если бы это был положительный ноль.   -  person Toby Speight    schedule 21.08.2015
comment
возможный дубликат как лучше всего избежать отрицательный ноль на выходе?   -  person Toby Speight    schedule 21.08.2015


Ответы (4)


Правильный ответ уже дан. Просто хотел добавить, что вы получите то же самое от c++ cout

Если вы хотите избавиться от знака, это можно сделать так:

double fixSign(double d)
{
    std::ostringstream strs;
    strs << std::fixed << std::setprecision(3) << d;
    std::string str = strs.str();

    if (str == "-0.000") return 0.0;

    return d;
}

int main()
{
    double d=-0.0001;

    printf("%.3lf\n", d);

    cout << std::fixed << std::setprecision(3) << d << endl;

    cout << std::fixed << std::setprecision(3) << fixSign(d) << endl;

    return 0;
}

выход:

-0.000
-0.000
0.000


EDIT Можно ли это сделать без преобразования в строку?

Как насчет:

#define PRE 3
#define LIMIT -0.0005  // Must have PRE zeros after the decimal point

// VERSION WITHOUT USE OF STRING
double fixSign_v2(double d)
{
    if ((d < 0) && (d > LIMIT)) return 0;

    return d;
}

double fixSign(double d)
{
    std::ostringstream strs;
    strs << std::fixed << std::setprecision(PRE) << d;
    std::string str = strs.str();

    if (str == "-0.000") return 0.0;

    return d;
}

int main()
{
    // PRE == 2
    //double d1=-0.005;
    //double d2=-0.0049999999999;

    // PRE == 3
    double d1=-0.0005;
    double d2=-0.000499999999999;

    // PRE == 10
    //double d1=-0.00000000005;
    //double d2=-0.0000000000499999999;

    cout << std::fixed << std::setprecision(PRE+20) << d1 << endl;

    cout << std::fixed << std::setprecision(PRE) << fixSign(d1) << endl;

    cout << std::fixed << std::setprecision(PRE) << fixSign_v2(d1) << endl;

    cout << "------------------------" << endl;

    cout << std::fixed << std::setprecision(PRE) << d2 << endl;

    cout << std::fixed << std::setprecision(PRE) << fixSign(d2) << endl;

    cout << std::fixed << std::setprecision(PRE) << fixSign_v2(d2) << endl;

    return 0;
}

выход:

-0.001
-0.001
-0.001
------------------------
-0.000
0.000
0.000

так вроде работает!

Но это не будет работать для всех режимов округления.

Поэтому безопаснее использовать первую версию с преобразованием строк.

person 4386427    schedule 21.08.2015
comment
Действительно? Преобразование в строку, сравнение строки и в зависимости от того, что возвращает определенное значение? Плюс: это будет работать, только если точность не изменится. - person Simon Kraemer; 21.08.2015
comment
@SimonKraemer - да, это единственный безопасный способ, который я мог придумать. Вы знаете лучший способ? Если вы измените точность, вам также придется изменить строку сравнения. В реальном коде вы, вероятно, где-нибудь поместите точность и сравните строку как константу вместо того, чтобы вводить 3several, но это всего лишь пример кода. - person 4386427; 21.08.2015
comment
Я разместил свое решение ниже. Просто округляем значение до требуемой точности перед его печатью. Таким образом, -0,0001 станет реальным 0 и, следовательно, будет напечатано правильно. - person Simon Kraemer; 21.08.2015

C++ наследует printf от C. В стандарте C11 §7.21.6.1 Функция fprintf сноска гласит:

Результаты всех плавающих преобразований отрицательного нуля и отрицательных значений, округляемых до нуля, включают знак минус.

person Yu Hao    schedule 21.08.2015
comment
как правильно печатать? без минуса? 0,000, а не -0,000 - person Md Tahsin Rahman; 21.08.2015

Представление с плавающей запятой ( https://en.wikipedia.org/wiki/Floating_point) имеет знак кусочек. Тот, кто реализовал printf, решил явно поставить его туда, даже когда все напечатанные числа равны 0.

person Chris Cinelli    schedule 21.08.2015

EDIT: Мое предыдущее решение не решило проблему должным образом.

template <int precision=0, double(*round_func)(double)=std::round>
double round(double a)
{
    auto multiplier = std::pow(10, precision);
    a *= multiplier;
    a = round_func(a);
    if (a == 0.0) return 0.0;
    a /= multiplier;
    return a;
}

int main()
{
    printf("printf: %.3lf\n", -0.0001);
    printf("round : %.3lf\n", round<3>(-0.0001));
    system("PAUSE");
}

Результат: printf: -0.000 раунд: 0.000

Сравнение printf с точностью и округлением

int main()
{
    printf("printf: %05.2lf\n", 12345.6789);                        
    printf("printf: %05.2lf\n", 12345.1234);                        
    printf("\n");
    printf("round : %05.2lf\n", round<2, std::round>(12345.6789));  
    printf("round : %05.2lf\n", round<2, std::round>(12345.1234));
    printf("\n");

    printf("floor : %05.2lf\n", round<2, std::floor>(12345.6789));  
    printf("floor : %05.2lf\n", round<2, std::floor>(12345.1234));
    printf("\n");

    printf("ceil  : %05.2lf\n", round<2, std::ceil>(12345.6789));   
    printf("ceil  : %05.2lf\n", round<2, std::ceil>(12345.1234));
    system("PAUSE");
}

Результат:

printf: 12345.68
printf: 12345.12

round : 12345.68
round : 12345.12

floor : 12345.67
floor : 12345.12

ceil  : 12345.68
ceil  : 12345.13

Таким образом, printf, кажется, также округляет значения при использовании точности.

---------------------------

Старый ответ, пожалуйста, игнорируйте:

Вы можете использовать это, чтобы округлить свой ответ перед его печатью:

template <int precision>
double round(double a)
{
    auto multiplier = std::pow(10, precision);
    a *= multiplier;
    a = std::round(a);
    a /= multiplier;
    return a;
}

В качестве альтернативы, если вы просто хотите вырезать остальное:

template <int precision>
double round_down(double a)
{
    auto multiplier = std::pow(10, precision);
    a *= multiplier;
    a = std::floor(a);
    a /= multiplier;
    return a;
}

Это работает даже с отрицательной точностью:

int main()
{
    //Round at precision
    printf("%5.4lf\n", round<4>(12345.6789));           //Output = 12345.6789
    printf("%5.4lf\n", round<3>(12345.6789));           //Output = 12345.6790
    printf("%5.4lf\n", round<2>(12345.6789));           //Output = 12345.6800
    printf("%5.4lf\n", round<1>(12345.6789));           //Output = 12345.7000
    printf("%5.4lf\n", round<0>(12345.6789));           //Output = 12346.0000
    printf("%5.4lf\n", round<-1>(12345.6789));          //Output = 12350.0000
    printf("%5.4lf\n", round<-2>(12345.6789));          //Output = 12300.0000
    printf("%5.4lf\n", round<-3>(12345.6789));          //Output = 12000.0000
    printf("%5.4lf\n", round<-4>(12345.6789));          //Output = 10000.0000
    printf("%5.4lf\n", round<-5>(12345.6789));          //Output = 0.0000

    //Cut off/Round down after precision
    printf("%5.4lf\n", round_down<4>(12345.6789));      //Output = 12345.6789
    printf("%5.4lf\n", round_down<3>(12345.6789));      //Output = 12345.6780
    printf("%5.4lf\n", round_down<2>(12345.6789));      //Output = 12345.6700
    printf("%5.4lf\n", round_down<1>(12345.6789));      //Output = 12345.6000
    printf("%5.4lf\n", round_down<0>(12345.6789));      //Output = 12345.0000
    printf("%5.4lf\n", round_down<-1>(12345.6789));     //Output = 12340.0000
    printf("%5.4lf\n", round_down<-2>(12345.6789));     //Output = 12300.0000
    printf("%5.4lf\n", round_down<-3>(12345.6789));     //Output = 12000.0000
    printf("%5.4lf\n", round_down<-4>(12345.6789));     //Output = 10000.0000
    printf("%5.4lf\n", round_down<-5>(12345.6789));     //Output = 0.0000
}

Альтернативное решение, позволяющее определить метод округления:

template <int precision=0, double(*round_func)(double)=std::round>
double round(double a)
{
    auto multiplier = std::pow(10, precision);
    a *= multiplier;
    a = round_func(a);
    a /= multiplier;
    return a;
}

int main()
{
    printf("%05.4lf\n", round(12345.6789));                     //Output = 12345.0000
    printf("%05.4lf\n", round<1>(12345.6789));                  //Output = 12345.7000
    printf("%05.4lf\n", round<1, std::round>(12345.6789));      //Output = 12345.7000
    printf("%05.4lf\n", round<1, std::floor>(12345.6789));      //Output = 12345.6000
    printf("%05.4lf\n", round<1, std::ceil>(12345.6789));       //Output = 12345.7000
}
person Simon Kraemer    schedule 21.08.2015
comment
Я не думаю, что 1001.0001 нужно округлять до 1000.0000. Также мне не нравится, что аргумент шаблона меняется. Наконец, я не уверен, что вся математика может дать неожиданное округление для некоторых значений точности и/или режимов округления. - person 4386427; 21.08.2015
comment
Можно округлить 1001,0001 до 1000 с точностью -3. Проверьте мой обновленный пример выше. Я не вижу в этом ничего плохого, особенно если он используется только для вывода. Исходное значение не изменяется. - person Simon Kraemer; 21.08.2015
comment
Теперь я понимаю, какова цель -3. Так что с этой частью все в порядке. Однако я не думаю, что ваш код будет работать. Цель исходного кода заключалась только в том, чтобы избавиться от знака -0.000. Для всех остальных значений результат должен быть точно таким же, как cout << someDouble с точностью 3. Независимо от текущего режима округления. Насколько я вижу, ваш код не может этого сделать. Также см. stackoverflow.com/questions/20264681/ - person 4386427; 21.08.2015
comment
printf с точностью выполняет округление, поэтому использование функции в сочетании с std::round дает тот же результат. Я обновлю свой ответ выше. - person Simon Kraemer; 21.08.2015
comment
Я думаю, мы должны положить этому конец. Я не согласен с вашим подходом, а вы не согласны с моим. Хорошо. Я закончу это, дав вам +1 за усилия. Повеселись :-) - person 4386427; 21.08.2015