tags:

views:

96

answers:

2

Hello all,

I have to convert a double to string with the following rules:

  1. If decimal point position is -1 (or another non-existing value meaning 'Auto'), fractional part of the number should be output with all significant digits (all zeroes at the end should be trimmed). If the double is integer, its fractional part shouldn't output at all. For instance, digits = -1: 1029.0 -> 1,029, 1029.123456789 -> 1,029.123456789.

  2. If decimal point position is equal or greater than 0, fractional part of the number should be output with the given number of digits. For instance, digits = 2: 1029.0 -> 1,029.00, 1029.123456789 -> 1,029.12.

  3. Conversion should be culture-dependant (point or comma as decimal point, comma or space as group divider etc).

I have a code for the task:

var _Culture = CultureInfo.CreateSpecificCulture("en-US");
object sourceValue = 1029.0;//.123456789;

int digits = -1; // 2;

var formatter = "G";
if (digits != -1)
{
    _Culture.NumberFormat.NumberDecimalDigits = digits;
    formatter = "N";
}

var sourceValueAsFloat = (double)sourceValue;
var s = sourceValueAsFloat.ToString(formatter, _Culture);

Is there another formatter (not "N" or "G"), I can use instead? Or, maybe, I can use "N"/"G" another way?

Regards,

A: 

See here and here for all the format string specifiers .net understands.

Alex Paven
I saw and have found nothing appropriate. It can mean 1. There is no suitable converter (then the question is "why?") or 2. I just haven't found it.
noober
There isn't a built-in format for this type of specific requirements. You can easily build a custom format string that respects all the requirements: if digits > -1 append '#' digits times, and that's that. "0.00" will output 2 digits even if they are 0. Then convert using the custom format string, for example "#.##" will not output digits for round doubles, but will round off to 2 decimal digits otherwise. If you convert without specifying the culture, the current culture is used. The decimal point and separators etc. will automatically be replaced with the culture specific ones.
Alex Paven
A: 
// preferably make allDigits a static field to avoid re-allocating on every call
string allDigits = "#,0." + new string('#', 350);

string output = sourceValue.ToString(
                    digits < 0 ? allDigits : "#,0." + new string('0', digits));

And if you need to handle different cultures explicitly:

string output = sourceValue.ToString(
                    digits < 0 ? allDigits : "#,0." + new string('0', digits),
                    culture);
LukeH
Your code is exactly what I had written just before the code, that is presented in the question. The only difference is you don't trim the decimal point itself (just zeros), and it breaks needed behavior. My humble opinion: this is worse than choosing between "N" and "G" as it requires "manual" operations (like trimming). And I'm looking for a better solution. No practical need, just academical interest :)
noober
@noober: I re-edited just before you posted your comment, so my code should be correct now, but it obviously still needs the manual checks/trim etc. I'm not aware of a single format specifier that'll do everything you need in one go; *"N"* is close, but defaults to the culture's default precision rather than the full precision of the number being formatted.
LukeH
Alocating a 2Mo string of '#' ... don't look like a good idea to me...
VirtualBlackFox
@VirtualBlackFox: Oops! I accidentally left that in there from my testing. `double.Epsilon` is only about 340 digits so I've knocked it down to something more sensible. (Having said that, 2MB is negligible on modern hardware, assuming that it's only allocated once. No point in allocating it for no reason though.)
LukeH