I'm working for several years in financial market and from times to times I need to treat specifically the decimal part of a value. When I get the value as a string I use to use Regex and when I get the value as a double or a decimal I use to use %, ceiling and pow.
// with math operations to get a truncated int value from the decimal part
// of a decimal or double
(int)Math.Ceiling((value % 1) * Math.Pow(10D, (double)(decimalLength - 1)))
// with Regex to get a truncated string value from the decimal part
// of a string
Regex.Replace(value, #"^[^.]+\.([0-9]{" + decimalLength.ToString() + "}).*$", "$1")
The question, as described at the headline, witch one is best? Should I convert my double or decimal values to string and always use Regex? Maybe I should convert my string to double and always use math operations? Or am I doing great dealing this way?
It's always better to use mathematical function to perform mathematical operations.
Not only does it make the code more readable, it's also likely to have better performance.
Therefor, as stribizhev wrote in the comments - Use #1 :).
Also, your regular expression does not take into consideration that different cultures might have different decimal separators.
Not everyone is writing one and a quarter as 1.25, some cultures use , as the decimal separator and thus it's written as 1,25.
Should you encounter a decimal like that, your regular expression will not work as you expect.
Another (quite easy method) which I'm sure people will state is bad or something (out of ignorance, mostly) is to convert it to a string and then split the string on the decimal point.
You can easily get the left and right parts of the decimal value in this manner, though it is not going to have the performance of the mathematical operations.
The nice thing of this method is that you don't need to use Math.Pow to get the decimal as a number that large, you can handle that in the .ToString portion.
int decimalPlaces = 10;
decimal number = 123456789.1234567890M;
string[] parts = number.ToString("F" + decimalPlaces).Split(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator[0]);
long[] lParts = new long[parts.Length];
for (int i = 0; i < parts.Length; i++)
lParts[i] = Convert.ToInt64(parts[i]);
for (int i = 0; i < parts.Length; i++)
Console.WriteLine(lParts[i].ToString());
Output:
123456789
1234567890
I'm not saying this is always the answer, but it can most certainly have it's advantages. It's also quite simple to use: all you have to do is change the 10 in the decimalPlaces to the number of decimal places you want, and you will have your same value. (Which of course, your method is just as modular.)
Merely putting this out here as another alternative to your methods, really the only bad method is using a Regex. If you wish to use strings then use regular string manipulation.
Update: Also, I just did a few benchmarks, on the Math method vs this string method, and found that the Math method can return slightly inaccurate results. When tested with double number = 9.1234567890D; the string method returned 1234567890, whereas the Math method returned 1234567891. This is something to keep in mind if you need the absolute and exact decimal values. The other issue is that the Math method cannot use a decimal type (which has greater precision than a double type) implicitly, it must be converted to a double first. The string method is, however, slower. It takes 250-300% of the time of the Math method.
These are just more things to keep in mind with it.
Related
I want to add a thousands separator to a double number but want to keep the decimal places as is i.e. dont want any rounding.
#,# is solving my problem of adding the thousand separator but how do I preserve the decimal places ? #,# strips off the part after ..
I cannot use any culture or something like that & the developer whose function I am calling has only given me a way of changing the format by passing as parameter strFormat.
I did check other posts & even the docs but somehow not able to figure this out.
string strFormat = "#,#";
string str = double.parse("912123456.1123465789").ToString(strFormat);
//Expected here 912,123,456.1123465789
//Actual Output 912,123,456
//912123456.123 should give 912,123,456.123
//912123456.1 should give 912,123,456.1
//912123456.1123465789 should give 912,123,456.1123465789
//912123456 should give 912,123,456
Well, after looking at the documentation on Microsoft, it would appear that there is no particular way to allow a floating point position in a number - all characters in a format string are character placeholders.
I would recommend that you either use a very nasty predetermined number of #s to set the width of the decimal position, or the slightly less (or possibly more, depending on your outlook) nasty option of reading all numbers into an array, determining the longest decimal position, then building a format of #s using the result.
At the end of the day, this is a single format string that you can put into place, test and ensure it works, then come back later and fix if you find a better alternative.
Also, this is one of those things where you could put the string into a configuration setting and change as and when you need to - far more flexible.
To be honest, this is a very slight thing to be worried about in the grand scheme of performance and writing a program.
Technically, Udi Y gets my vote!
If you know the max number of decimal places, e.g. 10, then use:
string strFormat = "#,#0.##########";
Update:
This max number is known.
According to Microsoft documentation a Double value has up to 15 decimal digits of precision (including both before and after the decimal point). More than 15 digits will be rounded.
So if you must invoke that method of 'double.parse' and can only send the format, this is the best you can do:
string strFormat = "#,#0.###############";
You can calculate the formatting dynamically for each number:
public static void Main()
{
var number = 1234.12312323123;
var format = GetNumberFormat(number);
Console.WriteLine(number.ToString(format));
}
public static string GetNumberFormat(double number)
{
var numberAsString = number.ToString();
var decimalPartSize = numberAsString.Substring(numberAsString.LastIndexOf('.') + 1).Length;
return $"N{decimalPartSize}";
}
So
number = 1234.12312323123
will give you 1,234.12312323123. Works for negative numbers as well. Also, as we work with strings, there won't be any rounding errors or precision artifacts.
I'm new to ASP.NET MVC C# Programming
I have a class which contains an attribute called Credits of type double. This attribute should be represented as a Time Format (hh:mm) in my web application. So for example, if I were to give Credits the value of 2.5, I want it to display 2:50, 6.15 as 6:15, etc. I know TimeSpan can convert a double to the time format but it converts 2.5 to 2:30. Is there a way to do easily convert a double to this time format without changing its data type to a String? Is there some sort of helper Attribute tag or something that would make this easy?
It appears that you want to get the whole number and the fractional part separated, and that you want the fractional part as a two-digit number.
So here's how you would get the whole number part as an int.
var whole = (int)Math.Truncate(value);
And this would give you the fractional part as an int, in this case only the first two decimal places.
var twoFrac = (int)(((decimal)value % 1) * 100);
So, if value was 12.345, then twoFrac would turn out to be 34 in int.
Now you can display these whichever way you like.
And you can actually put the above inside of a method so you can get the fractional part up to however many decimal places you want, like this:
private static int GetFractionalPart(double value, int decimalPlaces)
{
var factor = (int)Math.Pow(10, decimalPlaces);
var fractional = (int)(((decimal)value % 1) * factor);
return fractional;
}
NOTE
However, as many others pointed out in comments, this is a confusing and you could even say wrong way of doing things if you're actually dealing with time values. For example, using your criteria, a value of 2.75 would convert to 2:75, but that doesn't mean anything in terms of time.
So you really ought to use TimeSpan as you mentioned in your original post, which would convert 2.5 to 2:30 etc.
I am developing an application for some guys very picky about numerical accuracy (they are dealing among others with accounting and very accurate telecom variables). For this reason I am using the decimal type everywhere, because the float and double types are not suitable (do not hesitate to redirect me to an existing better datatype).
My problem comes when I need to format those numbers. The requirement is to display as many decimal digit as needed but at least 2 and also to use a group separator for thousands.
For example:
Value Formatted
1 1.00
1000 1,000.00
1.5 1.50
1000.355 1,000.355
0.000035 0.000035
So I went the MSDN looking for numeric string formats. I found this useful resource Standard Numeric Formats but none of my tries works as expected (N, N2, F, F2, G, G2, etc, I tried various combinations even when I didn't believe in them ^^ - I even try some F2# for fun).
My conclusion is there is not a built-in format to do what I want. Right?
So I checked out the next chapter Custom Numeric Formats. But I couldn't find a combination that suit my needs. So I went to SO and find a lots of question about that (1, 2, and so on).
These questions let me fear that the only solution is this one: #,##0.00####### with as many trailing # as I need precision.
Am I right?
I guess that with 12 #, my guys won't find any accuracy issue, but I might have missed the magical format I need?
This is probably what you're looking for:
static string FormatNumber(string input)
{
var dec = decimal.Parse(input);
var bits = decimal.GetBits(dec);
var prec = bits[3] >> 16 & 255;
if (prec < 2)
prec = 2;
return dec.ToString("N" + prec);
}
When you call it, do a ToString() on decimals, and convert the result back to decimal if needed.
I tried your example numbers, and the result:
Based on this SO answer.
I created a little function: it formats the number based on how many decimal places it has:
public static void Main()
{
decimal theValue;
theValue = 0.000035M;
Console.WriteLine(theFormat(theValue));
theValue = 1.5M;
Console.WriteLine(theFormat(theValue));
theValue = 1;
Console.WriteLine(theFormat(theValue));
}
public static decimal theFormat(decimal theValue){
int count = BitConverter.GetBytes(decimal.GetBits(theValue)[3])[2];
return count > 1?theValue:Convert.ToDecimal(string.Format("{0:F2}", theValue));
}
This, produces the following output:
0.000035
1.50
1.00
If you want a total control over formatting you can implement your own IFormatProvider for decimals. Inside of it you can use StringBuilder and do anything you need with no restrictions of string.Format().
Our middle tier sends us serialized objects and sometimes a 0, due to some math operations in java on the server, come through as 0E+3. When deserializing the object we get an XmlException --> System.OverflowException because the value is too large or small for a decimal.
Why can't decimal.Parse handle this conversion?
Is there a way to protect our client from these numbers coming in this way?
You could try:
decimal.Parse(numberText, System.Globalization.NumberStyles.Any)
EDIT:
This doesn't work for 0E+3 unfortunately
Works:
Console.WriteLine(decimal.Parse("0", System.Globalization.NumberStyles.Any));
Console.WriteLine(decimal.Parse("123.45", System.Globalization.NumberStyles.Any));
Console.WriteLine(decimal.Parse("1.35E+6", System.Globalization.NumberStyles.Any));
Console.WriteLine(decimal.Parse("1.54E-5", System.Globalization.NumberStyles.Any));
Doesn't work:
Console.WriteLine(decimal.Parse("0E+3", System.Globalization.NumberStyles.Any));
Is the problem number always 0E+3?
If so, you could write a helper method to handle this:
decimal ParseDecimal(string number)
{
if (number.Equals("0E+3", StringComparison.OrdinalIgnoreCase))
{
return 0;
}
return decimal.Parse(number, System.Globalization.NumberStyles.Any);
}
Java is probably doing this calculation as a double (which means you also don't need the extra precision of a Decimal) if they come out this way... consider using double instead of decimal.
If you have to, just trim out the E[+-]?([0-9]+)$ manually with a regex and do the multiplication yourself. Or match ^0E as a special case (it seems NumberStyles.Any can't handle it) and just return 0.
Change it to a float or double and it will parse it correctly. Although be careful, the precision will be much lower (7 digits for float or 15-16 digits for double vs 28-29 for decimal).
Does anyone know of an elegant way to get the decimal part of a number only? In particular I am looking to get the exact number of places after the decimal point so that the number can be formatted appropriately. I was wondering if there is away to do this without any kind of string extraction using the culture specific decimal separator....
For example
98.0 would be formatted as 98
98.20 would be formatted as 98.2
98.2765 would be formatted as 98.2765 etc.
It it's only for formatting purposes, just calling ToString will do the trick, I guess?
double d = (double)5 / 4;
Console.WriteLine(d.ToString()); // prints 1.75
d = (double)7 / 2;
Console.WriteLine(d.ToString()); // prints 3.5
d = 7;
Console.WriteLine(d.ToString()); // prints 7
That will, of course, format the number according to the current culture (meaning that the decimal sign, thousand separators and such will vary).
Update
As Clement H points out in the comments; if we are dealing with great numbers, at some point d.ToString() will return a string with scientific formatting instead (such as "1E+16" instead of "10000000000000000"). One way to overcome this probem, and force the full number to be printed, is to use d.ToString("0.#"), which will also result in the same output for lower numbers as the code sample above produces.
You can get all of the relevant information from the Decimal.GetBits method assuming you really mean System.Decimal. (If you're talking about decimal formatting of a float/double, please clarify the question.)
Basically GetBits will return you 4 integers in an array.
You can use the scaling factor (the fourth integer, after masking out the sign) to indicate the number of decimal places, but you should be aware that it's not necessarily the number of significant decimal places. In particular, the decimal representations of 1 and 1.0 are different (the former is 1/1, the latter is 10/10).
Unfortunately, manipulating the 96 bit integer is going to require some fiddly arithmetic unless you can use .NET 4.0 and BigInteger.
To be honest, you'll get a simpler solution by using the built in formatting with CultureInfo.InvariantCulture and then finding everything to the right of "."
Just to expand on the point about getbits, this expression gets the scaling factor from a decimal called foo:
(decimal.GetBits(foo)[3] & 16711680)>>16
You could use the Int() function to get the whole number component, then subtract from the original.