Convert a text fraction to a decimal - c#

Similar to this question just in C# instead of JavaScript. I couldn't find anything for C# while searching
I have a text box that will take a quantity, which is later stored as a double in a database. However it is possible that some quantities will be entered as string fractions e.g 1/2 for 0.5.
I want to be able to convert these to decimal before storing them to the database (being able to also convert back would be nice but not necessary). I want to be able to handle both fractions and mixed numbers e.g. 2 1/2 is saved as 2.5
Anybody know a way of doing this?

Try splitting it on the space and slash, something like:
double FractionToDouble(string fraction) {
double result;
if(double.TryParse(fraction, out result)) {
return result;
}
string[] split = fraction.Split(new char[] { ' ', '/' });
if(split.Length == 2 || split.Length == 3) {
int a, b;
if(int.TryParse(split[0], out a) && int.TryParse(split[1], out b)) {
if(split.Length == 2) {
return (double)a / b;
}
int c;
if(int.TryParse(split[2], out c)) {
return a + (double)b / c;
}
}
}
throw new FormatException("Not a valid fraction.");
}
Hey, it worked! Remember to check for a division by zero, too. You'll get Infinity, -Infinity, or NaN as a result.

And here is yet one more solution, but with a bit more seamless an integration:
public class FractionalNumber
{
public Double Result
{
get { return this.result; }
private set { this.result = value; }
}
private Double result;
public FractionalNumber(String input)
{
this.Result = this.Parse(input);
}
private Double Parse(String input)
{
input = (input ?? String.Empty).Trim();
if (String.IsNullOrEmpty(input))
{
throw new ArgumentNullException("input");
}
// standard decimal number (e.g. 1.125)
if (input.IndexOf('.') != -1 || (input.IndexOf(' ') == -1 && input.IndexOf('/') == -1 && input.IndexOf('\\') == -1))
{
Double result;
if (Double.TryParse(input, out result))
{
return result;
}
}
String[] parts = input.Split(new[] { ' ', '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
// stand-off fractional (e.g. 7/8)
if (input.IndexOf(' ') == -1 && parts.Length == 2)
{
Double num, den;
if (Double.TryParse(parts[0], out num) && Double.TryParse(parts[1], out den))
{
return num / den;
}
}
// Number and fraction (e.g. 2 1/2)
if (parts.Length == 3)
{
Double whole, num, den;
if (Double.TryParse(parts[0], out whole) && Double.TryParse(parts[1], out num) && Double.TryParse(parts[2], out den))
{
return whole + (num / den);
}
}
// Bogus / unable to parse
return Double.NaN;
}
public override string ToString()
{
return this.Result.ToString();
}
public static implicit operator Double(FractionalNumber number)
{
return number.Result;
}
}
And because it implements the implicit operator, it can be used simply by:
Double number = new FractionalNumber("3 1/2");
Anything that can't be parsed returns the Double constant Double.NaN. Without getting in to a laundry list of possible inputs, this worked with some basics. Feel free to tweak the class to fit your needs.

Split the string on the "/" then divide the two numbers.
public float FractionToDecimal(String input)
{
String[] fraction = input.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
if (fraction.Length != 2)
{
throw new ArgumentOutOfRangeException();
}
Int32 numerator, denominator;
if (Int32.TryParse(fraction[0], out numerator) && Int32.TryParse(fraction[1], out denominator))
{
if (denominator == 0)
{
throw new InvalidOperationException("Divide by 0 occurred");
}
return (float)numerator / denominator;
}
throw new ArgumentException();
}

You might try something like this: http://csharpeval.codeplex.com/ or this: https://github.com/Giorgi/Math-Expression-Evaluator
Both libraries are full mathematical expression evaluators written in C#. They're probably overkill for your use case, but worth mentioning for the benefit of others.

decimal re = (decimal)1/7;
works for me.

Related

Round string before converting to numeric type

I have a number (just an example:
"3616,946489653802082229919075063226"
stored in a string. I want to convert it into decimal but get an OverflowException because the value is too long for decimal.
I want to round the string before converting it to fix the error.
How do I do this?
Parsing is culture specific. So , can be either decimal or thousand separator. If , is a thousand separator, it will be ignored:
"3616,946489653802082229919075063226" -> 3616946489653802082229919075063226m
And this value is over decimal.MaxValue so you have the exception thrown.
If , should be treated as a decimal separator, let system know it:
decimal result = decimal.Parse(source,
new NumberFormatInfo() { NumberDecimalSeparator = ","});
And you'll get 3616.9464896538020822299190751m
Your problem is not actually a rounding issue, it is trying to parse a decimal with a comma on a system which expects a dot as a decimal separator by default.
You could try this to make sure it is parsed well on all environments:
using System;
using System.Globalization;
public class Program
{
public static void Main()
{
var input = "3616,946489653802082229919075063226";
var result = decimal.Parse(input, new NumberFormatInfo() { NumberDecimalSeparator = ","});
Console.WriteLine(result);
}
}
https://dotnetfiddle.net/8iaL9d
Given an arbitrary number of decimal places in a format with a comma separating the units from the fractional part, you could solve it by doing the following:
class Program
{
static void Main(string[] args)
{
var decimalPlacesCount = 10;
var decimalSeparator = ',';
var parts = "3616,946489653802082229919075063226".Split(decimalSeparator);
var value = decimal.Parse(parts[0]);
if (parts.Length == 2)
{
value += decimal.Parse($"0.{parts[1].Substring(0, decimalPlacesCount)}");
}
Console.WriteLine(value);
}
}
This effectively rounds it to decimalPlacesCount.
While this wasn't your problem if anyone happens to stumble across this well-named question looking for a method that will actually round a string hopefully, the following will be helpful.
public static string RoundString(string value, int decimalPlaces)
{
StringBuilder returnValue = new StringBuilder(value);
int startIndex = 0;
int charIndex = 0;
while (charIndex < value.Length && startIndex == 0)
{
if (value[charIndex].ToString() == ".")
startIndex = charIndex + 1;
charIndex++;
}
if (int.Parse(value[charIndex + decimalPlaces + 1].ToString()) >= 5)
{
bool rounded = false;
for (charIndex = startIndex + decimalPlaces; charIndex > -1; charIndex--)
{
if (!rounded && charIndex != startIndex-1)
{
int newVal = int.Parse(returnValue[charIndex].ToString()) + 1;
if (newVal > 9)
{
returnValue[charIndex] = '0';
}
else
{
returnValue[charIndex] = (int.Parse(returnValue[charIndex].ToString()) + 1).ToString()[0];
rounded = true;
}
}
}
if (!rounded)
{
startIndex++;
returnValue = new StringBuilder("1" + returnValue.ToString());
}
}
return returnValue.ToString().Substring(0, startIndex + decimalPlaces);
}
It's pretty poorly written and I'm sure someone could do better, but it does the job. The StringBuilder is pretty shoddy for example and it can likely be made to run quicker.
Also, I do 0 validation on the actual input string.

Is there a better way to remove characters from an integer in C#?

I need to check if an integer has 6 characters, and if it does, remove the first three characters and return the resultant integer. Otherwise, just return the original integer. This is what i have, I would like to know if there is a better/faster/more efficient way in C#?
public static int MyMethod(int originalInt)
{
int outputInt = originalInt;
string temp = originalInt.ToString();
if (temp.Length == 6)
{
temp = temp.Substring(3);
if (!Int32.TryParse(temp, out outputInt))
{
outputInt = originalInt;
}
}
return outputInt;
}
Why use strings at all?
if (originalInt >= 100000 && originalInt < 1000000)
return originalInt % 1000;
return originalInt;
(Assuming originalInt is always positive)
Try This :
public static int MyMethod(int originalInt)
{
return (originalInt > 99999 && originalInt <1000000)?originalInt % 1000 : originalInt;
}
It returns the result that you need

C# How to format a double to one decimal place without rounding

I need to format a double value to one decimal place without it rounding.
double value = 3.984568438706
string result = "";
What I have tried is:
1)
result = value.ToString("##.##", System.Globalization.CultureInfo.InvariantCulture) + "%";
// returns 3.98%
2)
result = value.ToString("##.#", System.Globalization.CultureInfo.InvariantCulture) + "%";
// returns 4%
3)
result = value.ToString("##.0", System.Globalization.CultureInfo.InvariantCulture) + "%";
// returns 4.0%
4) (Following other suggestions)
value = (value / 100);
result = String.Format("{0:P1}", Math.Truncate(value * 10000) / 10000);
// returns 4.0%
result = string.Format("{0:0.0%}",value); // returns 4.0%
What I need to display is the value 3.9%
Thanks for any help in advance.
result=string.Format("{0:0.0}",Math.Truncate(value*10)/10);
I would make a utility method to handle this:
static double Truncate(double value, int digits)
{
double mult = System.Math.Pow(10.0, digits);
return System.Math.Truncate(value * mult) / mult;
}
You could then do:
result = Truncate(value, 1).ToString("##.#", System.Globalization.CultureInfo.InvariantCulture) + "%";
Note that you may also want Math.Floor instead of truncate - but it depends on how you want negative values handled.
I know this is a old thread but I've just had to do this. While the approaches here work I want a easy way to be able to affect a lot of calls so using the Math.Truncate on all the calls to string.format wasn't really a good option.
Thus, I made a custom format provider which would allow me to add truncation to the formatting string, eg
string.format(new FormatProvider(), "{0:T}", 1.1299); // 1.12
string.format(new FormatProvider(), "{0:T(3)", 1.12399); // 1.123
string.format(new FormatProvider(), "{0:T(1)0,000.0", 1000.9999); // 1,000.9
The implementation is pretty simple and is easily extendible to other requirements.
public class FormatProvider : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof (ICustomFormatter))
{
return this;
}
return null;
}
public string Format(string format, object arg, IFormatProvider formatProvider)
{
if (arg.GetType() != typeof (double))
{
try
{
return HandleOtherFormats(format, arg);
}
catch (FormatException e)
{
throw new FormatException(string.Format("The format of '{0}' is invalid.", format));
}
}
if (format.StartsWith("T"))
{
int dp = 2;
int idx = 1;
if (format.Length > 1)
{
if (format[1] == '(')
{
int closeIdx = format.IndexOf(')');
if (closeIdx > 0)
{
if (int.TryParse(format.Substring(2, closeIdx - 2), out dp))
{
idx = closeIdx + 1;
}
}
else
{
throw new FormatException(string.Format("The format of '{0}' is invalid.", format));
}
}
}
double mult = Math.Pow(10, dp);
arg = Math.Truncate((double)arg * mult) / mult;
format = format.Substring(idx);
}
try
{
return HandleOtherFormats(format, arg);
}
catch (FormatException e)
{
throw new FormatException(string.Format("The format of '{0}' is invalid.", format));
}
}
private string HandleOtherFormats(string format, object arg)
{
if (arg is IFormattable)
{
return ((IFormattable) arg).ToString(format, CultureInfo.CurrentCulture);
}
return arg != null ? arg.ToString() : String.Empty;
}
}
ToString() doesn't do it. You have to add extra code. The other answers show math approaches, my approach below is kind of outside-the-box.
string result = value.ToString();
Console.WriteLine("{0}", result.Substring(0, result.LastIndexOf('.') + 2));
This is a fairly simple brute force approach, but it does the trick when the decimal is a '.'. Here's an extension method to ease the pain (and deals with the decimal point).
public static class Extensions
{
public static string ToStringNoTruncate(this double me, int decimalplaces = 1)
{
string result = me.ToString();
char dec = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator[0];
return result.Substring(0, result.LastIndexOf(dec) + decimalplaces + 1);
}
}
( Math.Truncate( ( value * 10 ) ) / 1000 ).ToString( "#.#%" )
Just use modulo operator + built in ToString:
result = (value - (value % 0.1)).ToString("N1") + "%";

Formatting double for showing only 4 digits

Is there a way to format a double number that always have n digits sepecified by user?
For example if user want to see always 4 digits, take the following numbers as example:
Original Formatted
------- ---------
3.42421 3.424
265.6250 265.6
812.50 812.5
12.68798 12.68
0.68787 0.687
I made up this but it just allows for number of floating points! it is not what I wanted!
public string ToEngV(double d, int percision = 0)
{
string zeros = string.Empty;
if (percision <= 0)
{
zeros += "0";
}
else if (percision > 0)
{
for (int i = 0; i < percision; i++)
{
zeros += "0";
}
}
return String.Format("{0:0." + zeros + "}", d)
}
Imagine I call the above method for a number like 812.50 and I set the precision to (this is now used for all numbers I am going to format). Obviously the output will be 812.5
But if I give the another number like 1.61826 I will get 1.6 and this ruins the formatting in the page I show these number to users. I need that to be 1.618
Thus I want my method to always show N digit!
I'm not sure if your asking to round or truncate numbers, so I wrote this method:
public static string ToEngV(this double d, int digits, bool round)
{
var lenght = Math.Truncate(d).ToString().Length;
if (lenght > digits)
{
throw new ArgumentException("...");
}
int decimals = digits - lenght;
if (round)
{
return Math.Round(d, decimals).ToString();
}
else
{
int pow = (int)Math.Pow(10, decimals);
return (Math.Truncate(d * pow) / pow).ToString();
}
}
Example:
var numbers = new double[] { 3.42421, 265.6250, 812.50, 12.68798, 0.68787 };
foreach (var number in numbers)
{
Console.WriteLine(number.ToEngV(4, false));
}
Console.WriteLine()
foreach (var number in numbers)
{
Console.WriteLine(number.ToEngV(4, true));
}
Output:
3.424
265.6
812.5
12.68
0.687
3.424
265.6
812.5
12.69
0.688
Note that if your number has more integer digits than digits you will get an ArgumentException.
number.ToString("#0.000").Substring(0, 5);
I'm not sure this is what you're searching for, anyway give it a try:
string FmtDbl(double num, int digits)
{
digits++; // To include decimal separator
string ret = num.ToString();
if (ret.Length > digits) return ret.Substring(0, digits);
else return ret + new String('0', digits - ret.Length);
}
Note that if your number has more than digits integer digits, this doesn't work...
What about something like:
d.ToString().PadRigth(4,'0').SubString(0,4);
public static void RunSnippet()
{
Console.WriteLine(myCustomFormatter(3.42421));
Console.WriteLine(myCustomFormatter(265.6250));
Console.WriteLine(myCustomFormatter(812.50));
Console.WriteLine(myCustomFormatter(12.68798));
Console.WriteLine(myCustomFormatter(0.68787));
Console.ReadLine();
}
public static double myCustomFormatter(double value)
{
string sValue = value.ToString();
string sFormattedValue = sValue.Substring(0,5);
double dFormattedValue= Convert.ToDouble(sFormattedValue);
return dFormattedValue;
}

C# equivalent of BigDecimal.stripTrailingZeros()

In Java you can use BigDecimal.stripTrailingZeros() to remove any extra zeros at the end.
I've read a few questions on here about how to strip trailing zeros from a decimal in C# but none of them seem to offer correct solutions.
This question for example the answer of doing ToString("G") doesn't always work.
Ideally I would like a function that does decimal -> decimal with the new decimal having the least scale possible without losing any info or removing any trailing zeros.
Is there any way to do this easily? Or would it involve fiddling around with decimal.GetBits()?
EDIT: Should also add I have i18n to worry about so doing string manipulation on the result isn't ideal because of differences in decimal separators.
How about Decimal.Parse(d.ToString("0.###########################"))?
Here is what i have come up with (Crazy code but works smoothly)
private decimal Normalize(decimal d)
{
string[] tmp = d.ToString().Split('.');
string val = tmp[0];
string fraction = null;
decimal result;
if(tmp.Length > 1) fraction = tmp[1];
if(fraction != null && Getleast(fraction) > 0)
{
decimal.TryParse(val.ToString() + "." + fraction.TrimEnd('0').ToString(),out result);
}
else
{
return decimal.Parse(val);
}
return result;
}
private decimal Getleast(string str)
{
decimal res;
decimal.TryParse(str.TrimEnd('0'),out res);// It returns 0 even if we pass null or empty string
return res;
}
Here are sample Input:
Console.WriteLine(Normalize(0.00M));
Console.WriteLine(Normalize(0.10M));
Console.WriteLine(Normalize(0001.00M));
Console.WriteLine(Normalize(1000.01M));
Console.WriteLine(Normalize(1.00001230M));
Console.WriteLine(Normalize(0031.200M));
Console.WriteLine(Normalize(0.0004000M));
Console.WriteLine(Normalize(123));
And respective output:
0
0.1
1
1000.01
1.0000123
31.2
0.0004
123
Not the most elegant of solutions but it should do everything you need.
private decimal RemoveTrailingZeros(decimal Dec)
{
string decString = Dec.ToString();
if (decString.Contains("."))
{
string[] decHalves = decString.Split('.');
int placeholder = 0, LoopIndex = 0;
foreach (char chr in decHalves[1])
{
LoopIndex++;
if (chr != '0')
placeholder = LoopIndex;
}
if (placeholder < decHalves[1].Length)
decHalves[1] = decHalves[1].Remove(placeholder);
Dec = decimal.Parse(decHalves[0] + "." + decHalves[1]);
}
return Dec;
}
Fixed version, Thanks Shekhar_Pro!

Categories

Resources