String.Format - Как я могу отформатировать до x цифр (независимо от десятичного знака)?

Мне нужно отформатировать число с плавающей запятой до x символов (6 в моем случае, включая десятичную точку). Мой вывод также должен включать знак числа

Итак, учитывая входные данные, вот ожидаемые результаты

1.23456   => +1.2345

-12.34567 => -12.345

-0.123456 => -0.1234

1234.567  => +1234.5

Пожалуйста, предполагайте, что перед последним символом всегда стоит десятичный знак. т.е. не будет ввода числа 12345.6 - ввод всегда будет меньше или равен 9999.9.

Я думаю, что это должно быть сделано условно.


person Simon    schedule 03.08.2012    source источник
comment
что вы ожидаете за 1234567.00? +1234567?   -  person Dmitry Khryukin    schedule 03.08.2012
comment
@BurundukXP Еще немного пояснений: предположим, что входных данных выше 9999 не будет.   -  person Simon    schedule 03.08.2012
comment
@ Саймон, можете ли вы подтвердить, что хотите усечения, а не округления?   -  person Jonathon Reinhart    schedule 03.08.2012
comment
Либо все в порядке, округление или усечение здесь не проблема, просто форматирование.   -  person Simon    schedule 03.08.2012
comment
@Simon Я опубликовал ответ, который должен работать для всех входных данных и всех x.   -  person Michael Graczyk    schedule 03.08.2012


Ответы (7)


Вот прозрачный способ сделать это без строк формата (кроме «F»):

  static void Main()
  {
     double y = 1.23456;
     Console.WriteLine(FormatNumDigits(y,5));
     y= -12.34567;
     Console.WriteLine(FormatNumDigits(y,5));
     y = -0.123456;
     Console.WriteLine(FormatNumDigits(y,5));
     y = 1234.567;
     Console.WriteLine(FormatNumDigits(y,5));

     y = 0.00000234;
     Console.WriteLine(FormatNumDigits(y,5));

     y = 1.1;
     Console.WriteLine(FormatNumDigits(y,5));
  }


  public string FormatNumDigits(double number, int x) {
     string asString = (number >= 0? "+":"") + number.ToString("F50",System.Globalization.CultureInfo.InvariantCulture);

     if (asString.Contains('.')) {
        if (asString.Length > x + 2) {
           return asString.Substring(0, x + 2);
        } else {
           // Pad with zeros
           return asString.Insert(asString.Length, new String('0', x + 2 - asString.Length));
        }
     } else {
        if (asString.Length > x + 1) {
           return asString.Substring(0, x + 1);
        } else {
           // Pad with zeros
           return asString.Insert(1, new String('0', x + 1 - asString.Length));
        }
     }
  }

Выход:

  +1.2345
  -12.345
  -0.1234
  +1234.5
  +0.0000
  +1.1000

ИЗМЕНИТЬ

Обратите внимание, что он не отсекает конечные нули.

person Michael Graczyk    schedule 03.08.2012
comment
Это отличается от моего, чем? - person Jonathon Reinhart; 03.08.2012
comment
@JonathonReinhart Попробуйте это: Console.WriteLine(FormatNumDigits(1.1, 5)); Console.WriteLine(ToStringWithSign(1.1, 5)); - person Michael Graczyk; 03.08.2012
comment
Ах, я не видел EDIT до этого комментария. - person Jonathon Reinhart; 03.08.2012

Вы упоминаете «x символов». Таким образом, мы можем упростить это до «x-1 цифр» и просто написать код, который показывает x цифр.

Я думаю передать спецификатор числового формата "G" в Double.ToString() максимально приближен к встроенному.

double d = 1234.56789;
string s = d.ToString("G6");           // "1234.57"

Поэтому мы просто расширяем это, чтобы вручную добавить «+» спереди:

if (d > 0)
    s = "+" + s;

Собираем все вместе в методе расширения:

EDIT: включает необязательный параметр для усечения

public static string ToStringWithSign(this double d, int digits, bool truncate = false)
{
    if (truncate) {
        double factor = Math.Pow(10, digits - 1);
        d = Math.Truncate(d * factor) / factor;
    }

    string s = d.ToString("G" + digits);
    if (d > 0)
        s = "+" + s;
    return s;
}

Полученные результаты:

(1234.56789).ToStringWithSign(4);      // "+1235"
(1234.56789).ToStringWithSign(5);      // "+1234.6"
(1234.56789).ToStringWithSign(6);      // "+1234.57"
(-1234.56789).ToStringWithSign(6);     // "-1234.57"

(1.2345678).ToStringWithSign(6);       // "+1.23457"
(1.2345678).ToStringWithSign(6, true); // "+1.23456"
person Jonathon Reinhart    schedule 03.08.2012
comment
Я бы изменил эту проверку, чтобы сделать, если d > 0. В некоторых культурах не используется - для отрицательного знака. - person Esteban Araya; 03.08.2012
comment
@EstebanAraya Хороший звонок. Если ничего другого, это проще :-P. Но я думаю, что если мы привнесем культуру в это обсуждение, у ОП возникнут другие проблемы из-за этого конкретного форматирования, которого он все равно желает. - person Jonathon Reinhart; 03.08.2012
comment
@JonathonReinhart Это все еще не совсем правильно, потому что значение округляется. - person Michael Graczyk; 03.08.2012
comment
Округление @MichaelGraczyk никогда не упоминалось в ОП. - person Jonathon Reinhart; 03.08.2012
comment
@JonathonReinhart Все его примеры усекаются, а не округляются. - person Michael Graczyk; 03.08.2012
comment
Это хорошие люди. Округление не было проблемой. Спасибо всем, что ответили. - person Simon; 03.08.2012
comment
Я не уверен, как OP хочет, чтобы конечные 0 обрабатывались, но похоже, что ваше решение просто их отключит (0,1000 → 0,1) - person Parker; 03.08.2012
comment
@Parker прав, к сожалению, это решение действительно их нарезает. Я посмотрю на это дальше, однако, хотя это близко, это не совсем правильно. - person Simon; 03.08.2012

Если вы хотите усечение:

string str = number.ToString("+0.00000;-0.00000").Substring(0,7);

Если вы хотите округлить:

string str = number.ToString("+0.0000;-0.0000");

РЕДАКТИРОВАТЬ: если вы хотите, вы можете написать простую оболочку, которая принимает количество цифр в качестве параметра:

string FormatDecimal(decimal value, int digits )
{
    return value
        .ToString(String.Format("+0.{0};-0.{0}", new string('0',digits - 2)))
        .Substring(0,digits+1);
}
person Eren Ersönmez    schedule 03.08.2012
comment
Спасибо, Эрен, это очень близко и действительно сработает для моих входных данных, однако Майклс более общий. - person Simon; 03.08.2012
comment
@Саймон Спасибо. но я должен сказать, что ответ излишне и неэффективно дублирует то, что уже встроено в ToString. - person Eren Ersönmez; 03.08.2012
comment
Но его принимает произвольное количество цифр в качестве параметра. - person Jonathon Reinhart; 03.08.2012
comment
@ Саймон, ты мог бы просто изменить количество нулей, не так ли? но даже если вам нужен метод-оболочка, я бы не стал дублировать встроенный материал. Смотрите мою правку, так лучше? - person Eren Ersönmez; 03.08.2012

Я считаю, что если вы хотите вывести до 6 символов, включая десятичную точку, вы можете взглянуть на это

double val1 = -99.56789;
//string strval = val1.ToString("+#.######;-#.######");// add # for additional decimal places
string strval = val1.ToString("+#.000000;-#.000000");    
Console.WriteLine(strval.Substring(0,strval.Length >= 7 ? 7 : strval.Length));
 //output -99.567 if positive would output +99.567

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

В этом случае нет округления и соответствует вашим потребностям, надеюсь, это то, что вы ищете.

person V4Vendetta    schedule 03.08.2012
comment
Было бы неплохо узнать, что здесь не так, чтобы это можно было исправить - person V4Vendetta; 03.08.2012
comment
Это должно быть нормально при использовании 0 вместо #, например #.000000 - person V4Vendetta; 03.08.2012

Это должно делать то, что вы хотите. Однако он округляет последнюю цифру.

static string Format(double d)
{
    // define how many number should be visible
    int digits = 5;

    // Get the left part of the number (ignore negative sign for now)
    int s = Math.Abs(d).ToString("####0").Length;

    // Total size minus the amount of numbers left of the decimal point
    int precision = digits - s;

    // Left side for positive, right side for negative.
    // All it's doing is generating as many 0s as we need for precision.
    string format = String.Format("+###0.{0};-###0.{0}", new String('0', precision));
    return d.ToString(format);
}

Учитывая ваш ввод, это возвращает:

+1.2346
-12.346
-0.1235
+1234.6

Он будет обрабатывать нули без проблем:

+0.1000
+0.0000
+1000.0
person Parker    schedule 03.08.2012
comment
@Simon Попробуй с Console.WriteLine(Format(9999.9)); - person Michael Graczyk; 03.08.2012

Попробуй это:

        double myDouble = 1546.25469874;
        string myStr = string.Empty;
        if (myDouble > 0)
        {
            myStr = myDouble.ToString("+0.0###");
        }
        else
        {
            myStr = myDouble.ToString("0.0####");
        }
        if (myStr.Length > 7)
        {
            myStr = myStr.Remove(7).TrimEnd('.');
        }
person Adil Mammadov    schedule 03.08.2012
comment
он вернет +1.0. Разве это не то, что вы хотите? Но 1.005 вернет +1.005 - person Adil Mammadov; 03.08.2012
comment
Нет, к сожалению, мне всегда нужно число из 6 символов, т.е. оно должно возвращать 1.0000. Он отсекает конечные нули - person Simon; 03.08.2012
comment
замените # на нули, и это практически там. - person Simon; 03.08.2012
comment
да, если вам нужно, чтобы отображались 0, вы также должны заменить # на нули) - person Adil Mammadov; 03.08.2012

Как указано здесь в MSDN, "0" является заполнителем для цифра. Итак, все, что вам нужно, это:

myDecimal.ToString("000000");

or

myDecimal.ToString("######");

если вы не хотите, чтобы цифра присутствовала вместо нуля.

person Esteban Araya    schedule 03.08.2012
comment
Да, но, учитывая, что десятичный знак находится в неизвестной позиции, сколько там начальных/конечных нулей? - person Simon; 03.08.2012
comment
@Simon, ты можешь определить положение десятичной точки, разделив на десять, пока не перестанешь получать целое число. Затем вы можете построить строку формата, как описано выше, и поместить десятичный разряд в правильное место. Я не знаю, сможете ли вы сделать это с предопределенной строкой формата. Я думаю, вам, возможно, придется строить его каждый раз. Я все же буду думать. Хороший вопрос! - person Esteban Araya; 03.08.2012