Why does Convert.ToString format doubles in this way? [duplicate] - c#

This question already has answers here:
Double to string conversion without scientific notation
(18 answers)
Double ToString - No Scientific Notation [duplicate]
(1 answer)
Closed 9 years ago.
Consider the following code:
var rateUsed = 0.00000001;
var rateUsedConvertToString = Convert.ToString(rateUsed);
var rateUsedToString = rateUsed.ToString();
Instead of my strings being "0.00000001", they are both "1E-08". I don't have control over the initial type of rateUsed, it is a double, and I want the exact string representation of the double.
Why does this happen, and how would I get 0.00000001 as a string, if this started off as a double value?

If you want a specific format, then you need to tell it what you want via the parameter. The available standard formats are here, or you can use custom formats. Pass your chosen format to either ToString(format) or Convert.ToString(value, format).
Note; 1E-8 is exactly 0.00000001 - that is simply using exponential notation, i.e. this is 1×10-8
The default format is "general" which is described as:
Result: The most compact of either fixed-point or scientific notation
So yes, sometimes it will use scientific (aka exponential) notation - specifically, it will do so whenever that is shorter than the fixed-point notation. Maybe try using .ToString("F")

You may want to start off looking at When does Double.ToString() return a value in scientific notation?
With that said, as #MarcGravell inferred, 1E-08=0.00000001, it's just been displayed in scientific notation. If you're looking for something to output as a decimal number (typical) you may want to look in to formatting it differently. Such as:
var rateUsed = 0.00000001;
var rateUsedToStringFormatted = rateUsed.ToString("0.########"); // 0.00000001
Another option is to create a custom IFormatProvider/ICustomFormatter which would perform this conversion consistently. Then, instead of using .ToString("0.########") everywhere, you could supply .ToString(MyDoubleFormatter) for a more consistent output (and, should you change it at a later date, you're doing so in one location).

You probably want to use the fixed-point ("F") format specifier with a different overload of ToString, one which accepts the format specifier string:
int num = 175;
Console.WriteLine(num.ToString("F", CultureInfo.InvariantCulture)); // 175.00
Console.WriteLine(num.ToString("F3", CultureInfo.InvariantCulture)); // 175.000
Alternatively, you can use a custom format specifier:
Console.WriteLine(num.ToString("00000.00", CultureInfo.InvariantCulture)); // 00175.00
Additionally, as shown in these examples, whenever you are converting values to their text representations, consider using CultureInfo.InvariantCulture for data which should be stored independently of the current thread's culture (like when writing data to a file, for example).

Related

ParseExact cannot parse a string in RFC 3339 Internet Date/Time format

It seems that C# does not manage to parse a time in a valid RFC 3339 format:
DateTime.ParseExact("2019-12-31T00:00:00.123456789+01:00", "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffffzzz", null)
This line throws an exception, while this line works just fine:
DateTime.ParseExact("2019-12-31T00:00:00.1234567+01:00", "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffzzz", null)
So it seems there is a limit on milliseconds, but I cannot find out any documentation on that. Is this how it is supposed to be?
The reason want to parse this date is that I have have an input date field. We use OAS (Swagger) date-time format that quite clearly says that any date in RFC 3339 Internet Date/Time format should be valid. Now from the spec here section 5.6
time-secfrac = "." 1*DIGIT
As far as I understand this means that up to 9 digits should be allowed and to be 100% compliant we have to allow these inputs, but it does not seem that C# even supports that.
Any ideas on how to fix it?
Per MSDN specification, you can use only fffffff
The fffffff custom format specifier represents the seven most
significant digits of the seconds fraction; that is, it represents the
ten millionths of a second in a date and time value.
In your first example
DateTime.ParseExact("2019-12-31T00:00:00.123456789+01:00", "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffffzzz", null)
you are using fffffffff which is more precise for .NET custom date and time format strings
As far as I know, .NET supports seven most significant digits for milliseconds which is The "fffffff" custom format specifier are for.
The "fffffff" custom format specifier represents the seven most
significant digits of the seconds fraction; that is, it represents the
ten millionths of a second in a date and time value.
Although it's possible to display the ten millionths of a second
component of a time value, that value may not be meaningful. The
precision of date and time values depends on the resolution of the
system clock.
That means you are giving not meaningful data that are not supported for .NET Framework. I strongly suggest not doing that.
In addition to the information in the other answers, if you cannot change your input and you still want to parse it, you may use one of the following solutions:
If your input will always be in the same format (i.e., has 9 seconds-fraction digits), you could just remove the two extra ones and proceed to parse it:
string input = "2019-12-31T00:00:00.123456789+01:00";
input = input.Remove(27, 2);
// TODO: parse `input`.
If you don't know the number of the seconds-fraction digits beforehand, you may use something like this:
string input = "2019-12-31T00:00:00.123456789+01:00";
string format = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFFzzz";
var regEx = new Regex(#"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,7})\d*");
input = regEx.Replace(input, "$1");
DateTime parsedDate = DateTime.ParseExact(input, format, null);

Identifiying Exponential value in C#

I am integrating a library with my c# code. There is a decimal parameter i am using from the library. Parameter return usual decimal values mostly like: 0.24756400. But sometimes it returns values in Exponential format like: 0E-8.
When i try process the parameter in my code, for example when i parse it like this:
Decimal.Parse(paymentAuth.IyziCommissionFee);)
it gives me exception
Object reference not set to an instance of an object) when the value is in Exponential format.
If i can identify the value format (number or Exponential number) i can manage it.
So how can i identify if the value is in Exponential format? Alternatively, any suggestion to manage such situation?
You don't need to identify if it is an exponent or not
If you want to parse a string that could be an exponential format into a decimal you can use the overload of the Decimal.Parse method that takes a System.Globalization.NumberStyles parameter to do this.
var result = Decimal.Parse(
paymentAuth.IyziCommissionFee,
System.Globalization.NumberStyles.Float
);
This specifies that the incoming string is a float, which you can represent using the exponential format you're talking about.
You can read the MSDN docs about this overload and the NumberStyles type here :
Decimal.Parse(String, NumberStyles)
System.Globalization.NumberStyles

Get and format UTC Time corresponding to NodaTime ZonedDateTime value?

I have ZonedDateTime value. I need to obtain corresponding UTC time and to format it as ISO8601 string (without Time Zone).
What's the right "NodaTime way" of doing it?
I understand that I can use ZonedDateTime.ToDateTimeUtc() method to get .Net DateTime Kind of Utc. Should I do it and then just use ToString()?
Like
var myresult = resZonedDateTime.ToDateTimeUtc().ToString("s")
Should I really use "s" there?
There are a few different ways, depending on exactly what you're looking for.
First, you need to decide whether you want a value like 2014-10-30T16:46:49 or one like 2014-10-30T16:46:49Z. Both are allowed by ISO8601, but the trailing Z at the end is specifically used when the value is UTC. If you send the string elsewhere, the receiving party will not have to guess what the basis for the value is.
If you want the Z, then convert your ZonedDateTime to an Instant using the .ToInstant() method.
Instant instant = zdt.ToInstant();
If you don't want the Z, but still want the value to reflect UTC, then adjust your ZonedDateTime to UTC, then strip it down to the LocalDateTime using it's .LocalDateTime property.
LocalDateTime ldt = zdt.WithZone(DateTimeZone.Utc).LocalDateTime;
Next, you need to decide whether you want to format the string inline with the BCL-based API, or whether you want to use the pattern-based API. Noda Time supports both, which you can read about in the user guide.
The BCL-based API allows you to use methods you're already familiar with, such as .ToString() and .ToString("some format", someCulture) For example:
string s = instant.ToString();
or
string s = ldt.ToString("s", CultureInfo.InvariantCulture);
The pattern-based API separates the work of parsing the format string from doing the actual formatting, as two steps:
var pattern = InstantPattern.ExtendedIsoPattern;
var s = pattern.Format(instant);
It is much more efficient if you are going to be formatting many items. I typically use it when I'm working with LINQ. For example:
var pattern = InstantPattern.GeneralPattern;
var strings = instants.Select(pattern.Format);
or
var pattern = LocalDateTimePattern.GeneralIsoPattern;
var strings = ldts.Select(pattern.Format);
And finally, you need to think about the precision you want. In the pattern API, the "general" formats are precise to whole seconds. The "extended" formats include fractional seconds up to 7 decimal places. You could also create your own pattern using the Create... static methods on the various pattern classes.
If you are just using ToString, then keep in mind that the default format may or may not be what you are looking for. Again, you can pass a format string. For Instant values, "g" is precise to whole seconds, but you would use a custom string of "yyyy'-'MM'-'dd'T'HH':'mm':'ss;FFFFFFF'Z'" if you need precision. For LocalDateTime values, "s" is precise to whole seconds, and "o" is precise to 7 decimals.
Again, all of this is in the "Text" chapter of the user guide, which I would encourage you to read.

Is conversion from basic system types to string reversible?

When storing an object in a string, can I ensure that casting it back will perform successfully does it depend on something else?
DateTime dt = DateTime.UtcNow;
string pattern = dt.ToString();
DateTime retDt = DateTime.Parse(pattern);
This question is asking about the following object types:
TimeSpan
DateTime
int
long
float
double
I have tried reading several forums and saw no contradicting example. I also haven't
got any exceptions from my code yet, but I am sure I haven't tried all possible inputs/scenarios.
When using ToString you can specify a format (see this topic for more information) for numbers and time.
The format you would want to use in many these cases is the round-trip format ("R" for Single, Double, and BigInteger types, "O" for DateTime.) This format ensures all the information passes to the string, and can be parsed back to the exact same data.
Passing an IFormatProvider to ToString is also essential to avoid problems with locales (for example, different locales can use different characters for the decimal separator.) Using the static CultureInfo.InvariantCulture solves this problem.
Lastly, if you're trying to persist data and then retrieve it, you may want to consider using a serializer, which takes entire classes and writes them to various formats. .NET has several serializers, two of the most prominent ones being the BinaryFormatter (binary) and the DataContractSerializer (XML).
Parse and ToString being able to convert to and from the type of your choice depends on their implementation. For the default the conversion will work without any change.
Take care with DateTime though as you might want to specify to output the timezone information with ToString so that your parse will create the new DateTime with the correct time zone information.
You need to be aware that culture specific settings come into play when using the default ToString implementation for these types.
If you always deserialize on the same machine that did the serialization, it should not be an issue (unless the user can change their culture settings between serialization and deserialization).
If you intend to deserialize on a different machine you should try to use a culture invariant representation. All of these types have overloads of ToString that take a format specifier. Unfortunately, the culture invariant specifiers are all slightly different. For the numeric types it is "r" (the "round-trip" format). For DateTime, it is "o". For TimeSpan it is "c". Typically the Parse methods accept current culture format or invariant format. Watch out for ParseExact methods.

Convert numbers with exponential notation from string to double or decimal

Is there a fast way to convert numbers with exponential notation (examples: "0.5e10" or "-5e20") to decimal or double?
Update: I found Parse a Number from Exponential Notation but the examples won't work for me unless I specified a culture.
Solution:
double test = double.Parse("1.50E-15", CultureInfo.InvariantCulture);
If your culture uses . as the decimal separator, just double.Parse("1.50E-15") should work.
If your culture uses something else (e.g. ,) or you want to make sure your application works the same on every computer, you should use InvariantCulture:
double.Parse("1.50E-15", CultureInfo.InvariantCulture)
The standard double.Parse or decimal.Parse methods do the job here.
Examples:
// AllowExponent is implicit
var number1 = double.Parse("0.5e10");
Debug.Assert(number1 == 5000000000.0);
// AllowExponent must be given explicitly
var number2 = decimal.Parse("0.5e10", NumberStyles.AllowExponent);
Debug.Assert(number2 == 5000000000m);
Also, see the MSDN article Parsing Numeric Strings for more information. As long as the NumberStyles.AllowExponent option is specified to the Parse method (which it is by default for double), parsing such strings will work fine.
NB: As the questioner points out, the exponential notation of "e10" for example does not work in all cultures. Specifying en-US culture however ensures that it works. I suspect CultureInfo.InvariantCulture should also do the trick.
#Noldorin is correct try this code:
string str = "-5e20";
double d = double.Parse(str);
Console.WriteLine(str);
the Math.Round does it well, it will reder the number so that will remove, here is how to use it:
Math.Round(Double.Parse("3,55E-15"),2)

Categories

Resources