BigInteger string representation with decimal places - c#

I'm trying to create a decimal representation of a big integer when it's divided by something. The follwoing is the code which does it, basically I want the precision to be of 2 places.
public string GetDecimal(BigInteger bigInteger,int divisor)
{
var remainder = BigInteger.Remainder(bigInteger, divisor);
var dividend = BigInteger.Divide(bigInteger, divisor);
var d = ((double)remainder / divisor);
var decimalPart = Math.Round(d, 2);
var retValue = dividend + decimalPart.ToString(".00");
return retValue;
}
}
Is there a better way of doing this please?
Thanks,
-Mike

You should probably not convert the types and do the long division on your own. This should work with any BigInteger value.
I'm sure there's room for improvement here...
public string GetDecimal(BigInteger bigInteger, int divisor)
{
BigInteger remainder;
var quotient = BigInteger.DivRem(bigInteger, divisor, out remainder);
const int decimalPlaces = 2;
var decimalPart = BigInteger.Zero;
for(int i = 0; i < decimalPlaces; i++)
{
var div = (remainder*10)/divisor;
decimalPart *= 10;
decimalPart += div;
remainder = remainder*10 - div*divisor;
}
var retValue = quotient.ToString() + "." + decimalPart.ToString(new string('0', decimalPlaces));
return retValue;
}

The built in .net decimal type is 128bit, and works with similar rounding constructs of other types. Is this number not large enough?

Instead of doing maths on it especially using less accurate types like double.
simply build a string with all but the last two digits, append a decimal point and then put the last one in.
e.g. Something like
int precision = 2;
negative = 0;
if (bigInteger < 0)
{
negative = 1;
}
String strValue = bigInteger.ToString().PadRight(precision + negative + 1,'0');
return strValue.Insert(strValue.Length - precision, ".");

var s=((bigInteger*200+divisor)/(2*(BigInteger)divisor)).ToString();
return s.Insert(".",s.Length-2);
This code only works for positive values, and uses AwayFromZero midpoint rounding. I also didn't care about localization issues.

Related

Convert double to long without truncating

How to convert a double to a long without truncating it?
For example,
I want -26.3999745 to become -263999745
So far the methods I have tried such as Convert.ToInt64() truncate the number.
EDIT:
The number of decimal places varies, some of the numbers may have 5 decimal places
If you are really sure this is what you really want ..
long.Parse((-26.3999745).ToString().Replace(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator.ToString(), string.Empty)))
Following 2 steps
Get the numbers after the decimal n
Multiply by 10^n
Code:
double number = -26.3999745;
int n = BitConverter.GetBytes(decimal.GetBits((decimal)number)[3])[2];
long result = (long)(number * Math.Pow(10, n));
If you like to follow Single Responsibility (you should), then you can go with a second approach that is also not culture specific:
static int GetCountAfterDecimal(double number)
{
int count = 0;
string seperator = System.Globalization.CultureInfo.CurrentCulture
.NumberFormat.NumberDecimalSeparator;
string numberAsString = number.ToString();
int indexOfSeperator = numberAsString.IndexOf(seperator);
if (indexOfSeperator >= 0)
count = numberAsString.Length - indexOfSeperator - 1;
return count;
}
static long RemoveDecimalPoint(double number, int numbersCountAfterDecimal)
{
return (long)(number * Math.Pow(10, numbersCountAfterDecimal));
}
static void Main(string[] args)
{
double number = -26.3999745;
long result = RemoveDecimalPoint(number, GetCountAfterDecimal(number));
Console.WriteLine(result);
}
This might do the trick for you
double YourNumber = -26.3999745;
int DecimalPlacesCount = BitConverter.GetBytes(decimal.GetBits(Convert.ToDecimal(YourNumber))[3])[2];
long number = Convert.ToInt64("1".PadRight(DecimalPlacesCount + 1, '0'));
long kajs = (long) (YourNumber * number);

String/Int to double with precision defined onruntime

I have an input type integer that represents a number that needs to be converted to double between 1-100, and the rest is decimal precision.
Example: 1562 -> 15.62 ; 198912 -> 19.8912
Right now, I tried a conversion to string, count the number of characters, take 2 to check how many decimals I have and depending of the result "create" a composite string to get a valid double...
Any idea of there is a better way of resolving convert-precision on runtime.
What about this:
int value = 1562;
decimal d = value;
while (d > 100) {
d /= 10;
}
You can use LINQ Skip and Take like:
string str = "198912";
string newStr = string.Format("{0}.{1}", new string(str.Take(2).ToArray()), new string(str.Skip(2).ToArray()));
double d = double.Parse(newStr, CultureInfo.InvariantCulture);
You can add the checks for length on original string, and also use double.TryParse to see if you get valid values.
If you have an int to begin with then you can use decimal, which would provide you more accurate conversion. Like:
int number = 1562123123;
decimal decimalNumber = number;
while (decimalNumber > 100)
{
decimalNumber /= 10;
}
Here is a mathematical solution. The line lg = Math.Max(lg, 0); changes "2" to return "2.0" instead of "20.0" but I guess that depends on your needs for single digit numbers.
static double ToDoubleBetween1And100(int num)
{
var lg = Math.Floor(Math.Log10(num)) - 1;
lg = Math.Max(lg, 0);
return ((double)num) / Math.Pow(10, lg);
}

Truncating a number to specified decimal places

I need to truncate a number to 2 decimal places, which basically means
chopping off the extra digits.
Eg:
2.919 -> 2.91
2.91111 -> 2.91
Why? This is what SQL server is doing when storing a number of a
particular precision. Eg, if a column is Decimal(8,2), and you try to
insert/update a number of 9.1234, the 3 and 4 will be chopped off.
I need to do exactly the same thing in c# code.
The only possible ways that I can think of doing it are either:
Using the stringformatter to "print" it out only
two decimal places, and then converting it to a decimal,
eg:
decimal tooManyDigits = 2.1345
decimal ShorterDigits = Convert.ToDecimal(tooManyDigits.ToString("0.##"));
// ShorterDigits is now 2.13
I'm not happy with this because it involves a to-string and then
another string to decimal conversion which seems a bit mad.
Using Math.Truncate (which only accepts an integer), so I
can multiply it by 100, truncate it, then divide by 100. eg:
decimal tooLongDecimal = 2.1235;
tooLongDecimal = Math.Truncate(tooLongDecimal * 100) / 100;
I'm also not happy with this because if tooLongDecimal is 0,
I'll get a divide by 0 error.
Surely there's a better + easier way! Any suggestions?
You've answered the question yourself; it seems you just misunderstood what division by zero means. The correct way to do this is to multiply, truncate, then devide, like this:
decimal TruncateTo100ths(decimal d)
{
return Math.Truncate(d* 100) / 100;
}
TruncateTo100ths(0m); // 0
TruncateTo100ths(2.919m); // 2.91
TruncateTo100ths(2.91111m); // 2.91
TruncateTo100ths(2.1345m); // 2.13
There is no division by zero here, there is only division by 100, which is perfectly safe.
The previously offered mathematical solutions are vulnerable to overflow with large numbers and/or a large number of decimal places. Consider instead the following extension method:
public static decimal TruncateDecimal(this decimal d, int decimals)
{
if (decimals < 0)
throw new ArgumentOutOfRangeException("decimals", "Value must be in range 0-28.");
else if (decimals > 28)
throw new ArgumentOutOfRangeException("decimals", "Value must be in range 0-28.");
else if (decimals == 0)
return Math.Truncate(d);
else
{
decimal integerPart = Math.Truncate(d);
decimal scalingFactor = d - integerPart;
decimal multiplier = (decimal) Math.Pow(10, decimals);
scalingFactor = Math.Truncate(scalingFactor * multiplier) / multiplier;
return integerPart + scalingFactor;
}
}
Usage:
decimal value = 18446744073709551615.262626263m;
value = value.TruncateDecimal(6); // Result: 18446744073709551615.262626
I agree with p.s.w.g. I had the similar requirement and here is my experience and a more generalized function for truncating.
http://snathani.blogspot.com/2014/05/truncating-number-to-specificnumber-of.html
public static decimal Truncate(decimal value, int decimals)
{
decimal factor = (decimal)Math.Pow(10, decimals);
decimal result = Math.Truncate(factor * value) / factor;
return result;
}
Using decimal.ToString('0.##') also imposes rounding:
1.119M.ToString("0.##") // -> 1.12
(Yeah, likely should be a comment, but it's hard to format well as such.)
public static decimal Rounding(decimal val, int precision)
{
decimal res = Trancating(val, precision + 1);
return Math.Round(res, precision, MidpointRounding.AwayFromZero);
}
public static decimal Trancating(decimal val,int precision)
{
if (val.ToString().Contains("."))
{
string valstr = val.ToString();
string[] valArr = valstr.Split('.');
if(valArr[1].Length < precision)
{
int NoOfZeroNeedToAdd = precision - valArr[1].Length;
for (int i = 1; i <= NoOfZeroNeedToAdd; i++)
{
valstr = string.Concat(valstr, "0");
}
}
if(valArr[1].Length > precision)
{
valstr = valArr[0] +"."+ valArr[1].Substring(0, precision);
}
return Convert.ToDecimal(valstr);
}
else
{
string valstr=val.ToString();
for(int i = 0; i <= precision; i++)
{
if (i == 1)
valstr = string.Concat(valstr, ".0");
if(i>1)
valstr = string.Concat(valstr, "0");
}
return Convert.ToDecimal(valstr);
}
}

Limit exponent only to specific powers

In our program we have values around millions to milliards which need to be displayed. Due to user requirements, the users want to see only exponent values which are dividable by 3. means the values to be displayed shall be like
1e0
10e0
100e0
1e3
10e3
100e3
1e6
and so on. I want to change exponent where thousands are encountered, to simplify the user the way to distinguish kilobits, megabits, gigabits.
Is it possible to get this effect with a format string?
Something like this
int val = 123456789;
var order = (int)Math.Log10(val);
var auxOrder = order/3;
var charToShow = val.ToString().Length - (auxOrder *3);
var result = val.ToString().Substring(0, charToShow )+"e"+(auxOrder *3);
There are better names for the variables like mantissa e exponent, but it will do the trick
The code from Servy was the starting point, but it still had some bugs, not working for negative or very small values. I expanded it like this
double exponent = Math.Log10(Math.Abs(val));
var negcompensator = 0;
if (exponent < 0)
{
negcompensator = -1;
}
int displayExponent = (int)(Math.Truncate((exponent + negcompensator) / 3)) * 3;
if (displayExponent==0 || double.IsInfinity(exponent))
{
return val.ToString("###0.##");
}
else
{
var displayValue = (val / Math.Pow(10, displayExponent));
return string.Format("{0}e{1}", displayValue, displayExponent);
}
I dislike solutions like this that involve string manipulation, so I calculated both portions mathematically here.
public static string MyFormat(int value)
{
double exponent = Math.Log10(value);
int displayExponent = (int)(exponent / 3);
int displayValue = (int)(value / Math.Pow(exponent, 10));
return string.Format("{0}e{1}", displayValue, displayExponent);
}

How do I round doubles in human-friendly manner in C#?

In my C# program I have a double obtained from some computation and its value is something like 0,13999 or 0,0079996 but this value has to be presented to a human so it's better displayed as 0,14 or 0,008 respectively.
So I need to round the value, but have no idea to which precision - I just need to "throw away those noise digits".
How could I do that in my code?
To clarify - I need to round the double values to a precision that is unknown at compile time - this needs to be determined at runtime. What would be a good heuristic to achieve this?
You seem to want to output a value which is not very different to the input value, so try increasing numbers of digits until a given error is achieved:
static double Round(double input, double errorDesired)
{
if (input == 0.0)
return 0.0;
for (int decimals = 0; decimals < 17; ++decimals)
{
var output = Math.Round(input, decimals);
var errorAchieved = Math.Abs((output - input) / input);
if (errorAchieved <= errorDesired)
return output;
}
return input;
}
}
static void Main(string[] args)
{
foreach (var input in new[] { 0.13999, 0.0079996, 0.12345 })
{
Console.WriteLine("{0} -> {1} (.1%)", input, Round(input, 0.001));
Console.WriteLine("{0} -> {1} (1%)", input, Round(input, 0.01));
Console.WriteLine("{0} -> {1} (10%)", input, Round(input, 0.1));
}
}
private double PrettyRound(double inp)
{
string d = inp.ToString();
d = d.Remove(0,d.IndexOf(',') + 1);
int decRound = 1;
bool onStartZeroes = true;
for (int c = 1; c < d.Length; c++ )
{
if (!onStartZeroes && d[c] == d[c - 1])
break;
else
decRound++;
if (d[c] != '0')
onStartZeroes = false;
}
inp = Math.Round(inp, decRound);
return inp;
}
Test:
double d1 = 0.13999; //no zeroes
double d2 = 0.0079996; //zeroes
double d3 = 0.00700956; //zeroes within decimal
Response.Write(d1 + "<br/>" + d2 + "<br/>" + d3 + "<br/><br/>");
d1 = PrettyRound(d1);
d2 = PrettyRound(d2);
d3 = PrettyRound(d3);
Response.Write(d1 + "<br/>" + d2 + "<br/>" + d3 +"<br/><br/>");
Prints:
0,13999
0,0079996
0,00700956
0,14
0,008
0,007
Rounds your numbers as you wrote in your example..
I can think of a solution though it isn't very efficient...
My assumption is that you can tell when a number is in the "best" human readable format when extra digits make no difference to how it is rounded.
eg in the example of 0,13999 rounding it to various numbers of decimal places gives:
0
0.1
0.14
0.14
0.14
0.13999
I'd suggest that you could loop through and detect that stable patch and cut off there.
This method seems to do this:
public double CustomRound(double d)
{
double currentRound = 0;
int stability = 0;
int roundLevel = 0;
while (stability < 3)
{
roundLevel++;
double current = Math.Round(d, roundLevel);
if (current == currentRound)
{
stability++;
}
else
{
stability = 1;
currentRound=current;
}
}
return Math.Round(d, roundLevel);
}
This code might be cleanable but it does the job and is a sufficient proof of concept. :)
I should emphasise that that initial assumption (that no change when rounding) is the criteria we are looking at which means that something like 0.3333333333 will not get rounded at all. With the examples given I'm unable to say if this is correct or not but I assume if this is a double issues that the problem is with the very slight variations from the "right" value and the value as a double.
Heres what I tried:
public decimal myRounding(decimal number)
{
double log10 = Math.Log10((double) number);
int precision = (int)(log10 >= 0 ? 0 : Math.Abs(log10)) + (number < 0.01m ? 1 : 2);
return Math.Round(number, precision);
}
test:
Console.WriteLine(myRounding(0.0000019999m)); //0.000002
Console.WriteLine(myRounding(0.0003019999m)); //0.0003
Console.WriteLine(myRounding(2.56777777m)); //2.57
Console.WriteLine(myRounding(0.13999m)); //0.14
Console.WriteLine(myRounding(0.0079996m)); //0.008
You can do it without converting to string. This is what I created fast:
private static double RoundDecimal(double number)
{
double temp2 = number;
int temp, counter = 0;
do
{
temp2 = 10 * temp2;
temp = (int)temp2;
counter++;
} while (temp < 1);
return Math.Round(number, counter < 2 ? 2 : counter);
}
or
private static double RoundDecimal(double number)
{
int counter = 0;
if (number > 0) {
counter = Math.Abs((int) Math.Log10(number)) + 1;
return Math.Round(arv, counter < 2 ? 2 : counter);
}
After giving it another thought I did the following and looks like it does what I want so far.
I iterate over the number of digits and compare Round( value, number ) and Round( value, number + 1 ). Once they are equal (not == of course - I compare the difference against a small number) then number is the number of digits I'm looking for.
Double.ToString() can take a string format as an argument. This will display as many characters as you require, rounding to the decimal place. E.G:
double Value = 1054.32179;
MessageBox.Show(Value.ToString("0.000"));
Will display "1054.322".
Source
Generic formats (i.e, pre-generated)
How to generate custom formats
You can use no of digits with Math.Round Function
Double doubleValue = 4.052102;
Math.Round(doubleValue, 2);
This will return 4.05 as your required answer.
This is tested code, can u explain me how i am wrong. So i need to change.

Categories