Efficiently convert large BigInteger to string, ToString() takes too long - c#

I was working on a factorial function, and factorial with big integers can get REALLY long.
For example, 200000! = 973350 digits long and if i just use ToString(), things take a very long time.
200000! takes longer to convert to string then actually compute it!
I've tried to set the factorial function to a Process, and then use ProcessorAffinity to pin the Thread to a specific core so that that core ONLY converts to string, but that just took exactly the same time.
Also, the reason I want to convert it to string is because I want to write the output (FactFile) to a text file.
String Conversion code:
using (Process proc = Process.GetCurrentProcess())
{
proc.ProcessorAffinity = (IntPtr)0x0003;
FactFile = Res.ToString(); //FactFile is Going to be the final string, Res is the Factorial.
}
here's my code for Factorial:
for (BigInteger i = 1; i < k; i++)
{
Res *= i; // Res is the number to Calculate the factorial of
}
200000! takes 15 seconds to compute, and then another 18 seconds to convert it to string (this can differ from cpu to cpu, I have an i7).
Reminder: What's the most efficient way to convert to string?
output:
Total Computation Time: 00:00:16.2007276
String Conversion Time: 00:00:19.4049292

Here is a 40% faster stringifier. It divides the big integer repeatedly with the number 10^10000, stringifies the remainder, and finally joins all the strings together. It can handle negative numbers also.
public static string ToDecimalString(this BigInteger value)
{
if (value == 0) return "0";
var digits = 10000;
var divider = BigInteger.Pow(10, digits);
var parts = new Stack<string>();
while (true)
{
BigInteger remainder;
value = BigInteger.DivRem(value, divider, out remainder);
if (value != 0)
{
parts.Push(BigInteger.Abs(remainder).ToString().PadLeft(digits, '0'));
}
else
{
parts.Push(remainder.ToString());
break;
}
}
return String.Join("", parts);
}
It can become slightly faster by offloading the stringifying of the remainders to a background thread. Unfortunately the slowest part of the algorithm (the call to BigInteger.DivRem) is not parallelizable.
public static string ToDecimalStringParallel(this BigInteger value)
{
if (value == 0) return "0";
var digits = 10000;
var divider = BigInteger.Pow(10, digits);
var remainders = new BlockingCollection<BigInteger>();
var parts = new ConcurrentStack<string>();
var task = Task.Run(() =>
{
foreach (var remainder in remainders.GetConsumingEnumerable())
{
parts.Push(BigInteger.Abs(remainder).ToString().PadLeft(digits, '0'));
}
});
while (true)
{
BigInteger remainder;
value = BigInteger.DivRem(value, divider, out remainder);
if (value != 0)
{
remainders.Add(remainder);
}
else
{
remainders.CompleteAdding();
task.Wait();
parts.Push(remainder.ToString());
break;
}
}
return String.Join("", parts);
}

For n! above n = 24, the result will have more digits than the value of input n.
That explosion of digits causes ToString to have to do more work than the factorial (n-1 multiplies vs more than n divides).
However because BigInteger operations take longer based on the magnitude of the numbers they work on, you can divide by a factor first, e.g. for your 200000! example:
var asString = Res.ToString()
Versus:
BigInteger[] bits = new BigInteger[2];
bits[0] = BigInteger.DivRem(Res, BigInteger.Pow(new BigInteger(10), 486675), out var remainder);
bits[1] = remainder;
var strs = new string[2];
System.Threading.Tasks.Parallel.For(0, 2, (i) =>
{
strs[i] = bits[i].ToString();
});
var asString = strs[0] + strs[1];
I found this to be 5 seconds faster but:
I had to choose a factor of 10^486675 to divide the digits equally - not sure how you'd do that in general
The initial DivRem takes 8 seconds
See also some existing attempts to speed up both factorial calculation and bigint to base 10 by breaking up the work.
The conclusion of the second one seemed to be.. don't convert large BigIntegers to base 10!

Related

C# - Class that uses ILists to store huge integers without BigInt. Can't figure out how to use CompareTo and Int.TryParse to +, -, and * two Lists

I've been working on an assignment and I'm a beginner to C#. I have to implement a program that's similar to what BigInt can do: perform addition, subtraction, or multiplication with two absurdly large values (without actually using the BigInt library). I was told to use CompareTo and that it would make creating the add, subtract, and multiply methods easy, but I have no clue how to implement CompareTo. I don't even know if my class is implemented correctly or if I am missing something important.
Here is my code:
public class HugeInt
{
char sign;
public IList<int> theInt = new List<int>();
public string ToString(IList<int> theInt)
{
string bigInt = theInt.ToString();
return bigInt;
}
public HugeInt CompareTo(HugeInt num1)
{
int numParse;
string number = ToString(theInt); /// I did this to convert the List into a string
for(int i = 0; i < number.Length; i++)
{
bool temp = Int32.TryParse(number, out numParse); /// Supposed to change each index of the string to a separate integer (not sure how to properly do this)
/// These are *supposed to* perform operations on two HugeInts ///
num1.plus(numParse, num1);
num1.minus(numParse, num1);
num1.times(numParse, num1);
}
return num1;
}
I'm not here to ask for all the answers for this assignment, I've just been working on this for hours now and can't figure out what I'm doing wrong -- I have already done a lot of google searching. Thanks in advance for all advice and help!
To write such a class, it requires you know a little bit about how to do math by hand. For example, when adding two numbers, you start by adding their least significant digits. If the result is greater than 9, you have to carry a 1 to the next digit (explanation). Then you continue to the next digit.
Now, here is my take on it. I want to save the "huge int" as a list of digits starting from the least significant digit. Then I implement the Plus method as described above. I can compare two "huge ints" by looking at the number of digits. The number with the most digits is the largest. In the case the number of digits are the same, I will need to compare each digit one-by-one, starting from the most significant digit.
The below is just something to get you started. It only handles positive integers and has Plus and CompareTo methods. Be aware there are plenty of corner cases that I have not taken care of.
It can be used like this:
var num1 = new HugeInt("11112222333399998888777123123");
var num2 = new HugeInt("00194257297549");
Console.WriteLine(num1.Plus(num2).ToString()); // Writes 11112222333399999083034420672
Console.WriteLine(num1.CompareTo(num2)); // Writes -1 since num1 > num2
Here is the class:
public class HugeInt
{
// The array that contains all the digits of the number. To create a new number, you do not change this array but instead you create a new instance of HugeInt.
// The first digit is the least significant digit.
private readonly int[] digits;
public HugeInt(string number)
{
// Trim off the leading zeros
number = number.TrimStart('0');
if (number == "")
number = "0";
// Convert to digit array with the least significant digit first
digits = number.ToCharArray().Select(c => int.Parse(c.ToString())).Reverse().ToArray();
}
public HugeInt(IList<int> digits)
{
// Trim off the leading zeros
var d = digits.ToList();
while (d.Count > 1 && d.Last() == 0)
d.RemoveAt(d.Count - 1);
// Convert to digit array with the least significant digit first
this.digits = d.ToArray();
}
public HugeInt Plus(HugeInt num)
{
// Add two positive integers by adding each digit together, starting with the least significant digit.
var result = new List<int>();
int carry = 0;
for (var i = 0; i < this.digits.Length || i < num.digits.Length; i++)
{
var digit1 = i < this.digits.Length ? this.digits[i] : 0;
var digit2 = i < num.digits.Length ? num.digits[i] : 0;
var digitResult = digit1 + digit2 + carry;
carry = 0;
if (digitResult >= 10)
{
digitResult -= 10;
carry = 1;
}
result.Add(digitResult);
}
if (carry > 0)
result.Add(carry);
return new HugeInt(result);
}
public int CompareTo(HugeInt num)
{
// First compare by length of number
if (this.digits.Length > num.digits.Length)
return -1;
else if (this.digits.Length < num.digits.Length)
return 1;
else
{
// If lengths are equal, then compare each digit - starting with the most significant digit.
for (var i = this.digits.Length - 1; i >= 0; i--)
{
var cmp = this.digits[i].CompareTo(num.digits[i]);
if (cmp != 0)
return cmp;
}
return 0;
}
}
public override string ToString()
{
return String.Join("", digits.Reverse());
}
}

C# Parse String To Double Without Scientific Notation [duplicate]

How to convert a double into a floating-point string representation without scientific notation in the .NET Framework?
"Small" samples (effective numbers may be of any size, such as 1.5E200 or 1e-200) :
3248971234698200000000000000000000000000000000
0.00000000000000000000000000000000000023897356978234562
None of the standard number formats are like this, and a custom format also doesn't seem to allow having an open number of digits after the decimal separator.
This is not a duplicate of How to convert double to string without the power to 10 representation (E-05) because the answers given there do not solve the issue at hand. The accepted solution in this question was to use a fixed point (such as 20 digits), which is not what I want. A fixed point formatting and trimming the redundant 0 doesn't solve the issue either because the max width for fixed width is 99 characters.
Note: the solution has to deal correctly with custom number formats (e.g. other decimal separator, depending on culture information).
Edit: The question is really only about displaing aforementioned numbers. I'm aware of how floating point numbers work and what numbers can be used and computed with them.
For a general-purpose¹ solution you need to preserve 339 places:
doubleValue.ToString("0." + new string('#', 339))
The maximum number of non-zero decimal digits is 16. 15 are on the right side of the decimal point. The exponent can move those 15 digits a maximum of 324 places to the right. (See the range and precision.)
It works for double.Epsilon, double.MinValue, double.MaxValue, and anything in between.
The performance will be much greater than the regex/string manipulation solutions since all formatting and string work is done in one pass by unmanaged CLR code. Also, the code is much simpler to prove correct.
For ease of use and even better performance, make it a constant:
public static class FormatStrings
{
public const string DoubleFixedPoint = "0.###################################################################################################################################################################################################################################################################################################################################################";
}
¹ Update: I mistakenly said that this was also a lossless solution. In fact it is not, since ToString does its normal display rounding for all formats except r. Live example. Thanks, #Loathing! Please see Lothing’s answer if you need the ability to roundtrip in fixed point notation (i.e, if you’re using .ToString("r") today).
I had a similar problem and this worked for me:
doubleValue.ToString("F99").TrimEnd('0')
F99 may be overkill, but you get the idea.
This is a string parsing solution where the source number (double) is converted into a string and parsed into its constituent components. It is then reassembled by rules into the full-length numeric representation. It also accounts for locale as requested.
Update: The tests of the conversions only include single-digit whole numbers, which is the norm, but the algorithm also works for something like: 239483.340901e-20
using System;
using System.Text;
using System.Globalization;
using System.Threading;
public class MyClass
{
public static void Main()
{
Console.WriteLine(ToLongString(1.23e-2));
Console.WriteLine(ToLongString(1.234e-5)); // 0.00010234
Console.WriteLine(ToLongString(1.2345E-10)); // 0.00000001002345
Console.WriteLine(ToLongString(1.23456E-20)); // 0.00000000000000000100023456
Console.WriteLine(ToLongString(5E-20));
Console.WriteLine("");
Console.WriteLine(ToLongString(1.23E+2)); // 123
Console.WriteLine(ToLongString(1.234e5)); // 1023400
Console.WriteLine(ToLongString(1.2345E10)); // 1002345000000
Console.WriteLine(ToLongString(-7.576E-05)); // -0.00007576
Console.WriteLine(ToLongString(1.23456e20));
Console.WriteLine(ToLongString(5e+20));
Console.WriteLine("");
Console.WriteLine(ToLongString(9.1093822E-31)); // mass of an electron
Console.WriteLine(ToLongString(5.9736e24)); // mass of the earth
Console.ReadLine();
}
private static string ToLongString(double input)
{
string strOrig = input.ToString();
string str = strOrig.ToUpper();
// if string representation was collapsed from scientific notation, just return it:
if (!str.Contains("E")) return strOrig;
bool negativeNumber = false;
if (str[0] == '-')
{
str = str.Remove(0, 1);
negativeNumber = true;
}
string sep = Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator;
char decSeparator = sep.ToCharArray()[0];
string[] exponentParts = str.Split('E');
string[] decimalParts = exponentParts[0].Split(decSeparator);
// fix missing decimal point:
if (decimalParts.Length==1) decimalParts = new string[]{exponentParts[0],"0"};
int exponentValue = int.Parse(exponentParts[1]);
string newNumber = decimalParts[0] + decimalParts[1];
string result;
if (exponentValue > 0)
{
result =
newNumber +
GetZeros(exponentValue - decimalParts[1].Length);
}
else // negative exponent
{
result =
"0" +
decSeparator +
GetZeros(exponentValue + decimalParts[0].Length) +
newNumber;
result = result.TrimEnd('0');
}
if (negativeNumber)
result = "-" + result;
return result;
}
private static string GetZeros(int zeroCount)
{
if (zeroCount < 0)
zeroCount = Math.Abs(zeroCount);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < zeroCount; i++) sb.Append("0");
return sb.ToString();
}
}
You could cast the double to decimal and then do ToString().
(0.000000005).ToString() // 5E-09
((decimal)(0.000000005)).ToString() // 0,000000005
I haven't done performance testing which is faster, casting from 64-bit double to 128-bit decimal or a format string of over 300 chars. Oh, and there might possibly be overflow errors during conversion, but if your values fit a decimal this should work fine.
Update: The casting seems to be a lot faster. Using a prepared format string as given in the other answer, formatting a million times takes 2.3 seconds and casting only 0.19 seconds. Repeatable. That's 10x faster. Now it's only about the value range.
This is what I've got so far, seems to work, but maybe someone has a better solution:
private static readonly Regex rxScientific = new Regex(#"^(?<sign>-?)(?<head>\d+)(\.(?<tail>\d*?)0*)?E(?<exponent>[+\-]\d+)$", RegexOptions.IgnoreCase|RegexOptions.ExplicitCapture|RegexOptions.CultureInvariant);
public static string ToFloatingPointString(double value) {
return ToFloatingPointString(value, NumberFormatInfo.CurrentInfo);
}
public static string ToFloatingPointString(double value, NumberFormatInfo formatInfo) {
string result = value.ToString("r", NumberFormatInfo.InvariantInfo);
Match match = rxScientific.Match(result);
if (match.Success) {
Debug.WriteLine("Found scientific format: {0} => [{1}] [{2}] [{3}] [{4}]", result, match.Groups["sign"], match.Groups["head"], match.Groups["tail"], match.Groups["exponent"]);
int exponent = int.Parse(match.Groups["exponent"].Value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
StringBuilder builder = new StringBuilder(result.Length+Math.Abs(exponent));
builder.Append(match.Groups["sign"].Value);
if (exponent >= 0) {
builder.Append(match.Groups["head"].Value);
string tail = match.Groups["tail"].Value;
if (exponent < tail.Length) {
builder.Append(tail, 0, exponent);
builder.Append(formatInfo.NumberDecimalSeparator);
builder.Append(tail, exponent, tail.Length-exponent);
} else {
builder.Append(tail);
builder.Append('0', exponent-tail.Length);
}
} else {
builder.Append('0');
builder.Append(formatInfo.NumberDecimalSeparator);
builder.Append('0', (-exponent)-1);
builder.Append(match.Groups["head"].Value);
builder.Append(match.Groups["tail"].Value);
}
result = builder.ToString();
}
return result;
}
// test code
double x = 1.0;
for (int i = 0; i < 200; i++) {
x /= 10;
}
Console.WriteLine(x);
Console.WriteLine(ToFloatingPointString(x));
The problem using #.###...### or F99 is that it doesn't preserve precision at the ending decimal places, e.g:
String t1 = (0.0001/7).ToString("0." + new string('#', 339)); // 0.0000142857142857143
String t2 = (0.0001/7).ToString("r"); // 1.4285714285714287E-05
The problem with DecimalConverter.cs is that it is slow. This code is the same idea as Sasik's answer, but twice as fast. Unit test method at bottom.
public static class RoundTrip {
private static String[] zeros = new String[1000];
static RoundTrip() {
for (int i = 0; i < zeros.Length; i++) {
zeros[i] = new String('0', i);
}
}
private static String ToRoundTrip(double value) {
String str = value.ToString("r");
int x = str.IndexOf('E');
if (x < 0) return str;
int x1 = x + 1;
String exp = str.Substring(x1, str.Length - x1);
int e = int.Parse(exp);
String s = null;
int numDecimals = 0;
if (value < 0) {
int len = x - 3;
if (e >= 0) {
if (len > 0) {
s = str.Substring(0, 2) + str.Substring(3, len);
numDecimals = len;
}
else
s = str.Substring(0, 2);
}
else {
// remove the leading minus sign
if (len > 0) {
s = str.Substring(1, 1) + str.Substring(3, len);
numDecimals = len;
}
else
s = str.Substring(1, 1);
}
}
else {
int len = x - 2;
if (len > 0) {
s = str[0] + str.Substring(2, len);
numDecimals = len;
}
else
s = str[0].ToString();
}
if (e >= 0) {
e = e - numDecimals;
String z = (e < zeros.Length ? zeros[e] : new String('0', e));
s = s + z;
}
else {
e = (-e - 1);
String z = (e < zeros.Length ? zeros[e] : new String('0', e));
if (value < 0)
s = "-0." + z + s;
else
s = "0." + z + s;
}
return s;
}
private static void RoundTripUnitTest() {
StringBuilder sb33 = new StringBuilder();
double[] values = new [] { 123450000000000000.0, 1.0 / 7, 10000000000.0/7, 100000000000000000.0/7, 0.001/7, 0.0001/7, 100000000000000000.0, 0.00000000001,
1.23e-2, 1.234e-5, 1.2345E-10, 1.23456E-20, 5E-20, 1.23E+2, 1.234e5, 1.2345E10, -7.576E-05, 1.23456e20, 5e+20, 9.1093822E-31, 5.9736e24, double.Epsilon };
foreach (int sign in new [] { 1, -1 }) {
foreach (double val in values) {
double val2 = sign * val;
String s1 = val2.ToString("r");
String s2 = ToRoundTrip(val2);
double val2_ = double.Parse(s2);
double diff = Math.Abs(val2 - val2_);
if (diff != 0) {
throw new Exception("Value {0} did not pass ToRoundTrip.".Format2(val.ToString("r")));
}
sb33.AppendLine(s1);
sb33.AppendLine(s2);
sb33.AppendLine();
}
}
}
}
The obligatory Logarithm-based solution. Note that this solution, because it involves doing math, may reduce the accuracy of your number a little bit. Not heavily tested.
private static string DoubleToLongString(double x)
{
int shift = (int)Math.Log10(x);
if (Math.Abs(shift) <= 2)
{
return x.ToString();
}
if (shift < 0)
{
double y = x * Math.Pow(10, -shift);
return "0.".PadRight(-shift + 2, '0') + y.ToString().Substring(2);
}
else
{
double y = x * Math.Pow(10, 2 - shift);
return y + "".PadRight(shift - 2, '0');
}
}
Edit: If the decimal point crosses non-zero part of the number, this algorithm will fail miserably. I tried for simple and went too far.
In the old days when we had to write our own formatters, we'd isolate the mantissa and exponent and format them separately.
In this article by Jon Skeet (https://csharpindepth.com/articles/FloatingPoint) he provides a link to his DoubleConverter.cs routine that should do exactly what you want. Skeet also refers to this at extracting mantissa and exponent from double in c#.
I have just improvised on the code above to make it work for negative exponential values.
using System;
using System.Text.RegularExpressions;
using System.IO;
using System.Text;
using System.Threading;
namespace ConvertNumbersInScientificNotationToPlainNumbers
{
class Program
{
private static string ToLongString(double input)
{
string str = input.ToString(System.Globalization.CultureInfo.InvariantCulture);
// if string representation was collapsed from scientific notation, just return it:
if (!str.Contains("E")) return str;
var positive = true;
if (input < 0)
{
positive = false;
}
string sep = Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator;
char decSeparator = sep.ToCharArray()[0];
string[] exponentParts = str.Split('E');
string[] decimalParts = exponentParts[0].Split(decSeparator);
// fix missing decimal point:
if (decimalParts.Length == 1) decimalParts = new string[] { exponentParts[0], "0" };
int exponentValue = int.Parse(exponentParts[1]);
string newNumber = decimalParts[0].Replace("-", "").
Replace("+", "") + decimalParts[1];
string result;
if (exponentValue > 0)
{
if (positive)
result =
newNumber +
GetZeros(exponentValue - decimalParts[1].Length);
else
result = "-" +
newNumber +
GetZeros(exponentValue - decimalParts[1].Length);
}
else // negative exponent
{
if (positive)
result =
"0" +
decSeparator +
GetZeros(exponentValue + decimalParts[0].Replace("-", "").
Replace("+", "").Length) + newNumber;
else
result =
"-0" +
decSeparator +
GetZeros(exponentValue + decimalParts[0].Replace("-", "").
Replace("+", "").Length) + newNumber;
result = result.TrimEnd('0');
}
float temp = 0.00F;
if (float.TryParse(result, out temp))
{
return result;
}
throw new Exception();
}
private static string GetZeros(int zeroCount)
{
if (zeroCount < 0)
zeroCount = Math.Abs(zeroCount);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < zeroCount; i++) sb.Append("0");
return sb.ToString();
}
public static void Main(string[] args)
{
//Get Input Directory.
Console.WriteLine(#"Enter the Input Directory");
var readLine = Console.ReadLine();
if (readLine == null)
{
Console.WriteLine(#"Enter the input path properly.");
return;
}
var pathToInputDirectory = readLine.Trim();
//Get Output Directory.
Console.WriteLine(#"Enter the Output Directory");
readLine = Console.ReadLine();
if (readLine == null)
{
Console.WriteLine(#"Enter the output path properly.");
return;
}
var pathToOutputDirectory = readLine.Trim();
//Get Delimiter.
Console.WriteLine("Enter the delimiter;");
var columnDelimiter = (char)Console.Read();
//Loop over all files in the directory.
foreach (var inputFileName in Directory.GetFiles(pathToInputDirectory))
{
var outputFileWithouthNumbersInScientificNotation = string.Empty;
Console.WriteLine("Started operation on File : " + inputFileName);
if (File.Exists(inputFileName))
{
// Read the file
using (var file = new StreamReader(inputFileName))
{
string line;
while ((line = file.ReadLine()) != null)
{
String[] columns = line.Split(columnDelimiter);
var duplicateLine = string.Empty;
int lengthOfColumns = columns.Length;
int counter = 1;
foreach (var column in columns)
{
var columnDuplicate = column;
try
{
if (Regex.IsMatch(columnDuplicate.Trim(),
#"^[+-]?[0-9]+(\.[0-9]+)?[E]([+-]?[0-9]+)$",
RegexOptions.IgnoreCase))
{
Console.WriteLine("Regular expression matched for this :" + column);
columnDuplicate = ToLongString(Double.Parse
(column,
System.Globalization.NumberStyles.Float));
Console.WriteLine("Converted this no in scientific notation " +
"" + column + " to this number " +
columnDuplicate);
}
}
catch (Exception)
{
}
duplicateLine = duplicateLine + columnDuplicate;
if (counter != lengthOfColumns)
{
duplicateLine = duplicateLine + columnDelimiter.ToString();
}
counter++;
}
duplicateLine = duplicateLine + Environment.NewLine;
outputFileWithouthNumbersInScientificNotation = outputFileWithouthNumbersInScientificNotation + duplicateLine;
}
file.Close();
}
var outputFilePathWithoutNumbersInScientificNotation
= Path.Combine(pathToOutputDirectory, Path.GetFileName(inputFileName));
//Create Directory If it does not exist.
if (!Directory.Exists(pathToOutputDirectory))
Directory.CreateDirectory(pathToOutputDirectory);
using (var outputFile =
new StreamWriter(outputFilePathWithoutNumbersInScientificNotation))
{
outputFile.Write(outputFileWithouthNumbersInScientificNotation);
outputFile.Close();
}
Console.WriteLine("The transformed file is here :" +
outputFilePathWithoutNumbersInScientificNotation);
}
}
}
}
}
This code takes an input directory and based on the delimiter converts all values in scientific notation to numeric format.
Thanks
try this one:
public static string DoubleToFullString(double value,
NumberFormatInfo formatInfo)
{
string[] valueExpSplit;
string result, decimalSeparator;
int indexOfDecimalSeparator, exp;
valueExpSplit = value.ToString("r", formatInfo)
.ToUpper()
.Split(new char[] { 'E' });
if (valueExpSplit.Length > 1)
{
result = valueExpSplit[0];
exp = int.Parse(valueExpSplit[1]);
decimalSeparator = formatInfo.NumberDecimalSeparator;
if ((indexOfDecimalSeparator
= valueExpSplit[0].IndexOf(decimalSeparator)) > -1)
{
exp -= (result.Length - indexOfDecimalSeparator - 1);
result = result.Replace(decimalSeparator, "");
}
if (exp >= 0) result += new string('0', Math.Abs(exp));
else
{
exp = Math.Abs(exp);
if (exp >= result.Length)
{
result = "0." + new string('0', exp - result.Length)
+ result;
}
else
{
result = result.Insert(result.Length - exp, decimalSeparator);
}
}
}
else result = valueExpSplit[0];
return result;
}
Being millions of programmers world wide, it's always a good practice to try search if someone has bumped into your problem already. Sometimes there's solutions are garbage, which means it's time to write your own, and sometimes there are great, such as the following:
http://www.yoda.arachsys.com/csharp/DoubleConverter.cs
(details: http://www.yoda.arachsys.com/csharp/floatingpoint.html)
string strdScaleFactor = dScaleFactor.ToString(); // where dScaleFactor = 3.531467E-05
decimal decimalScaleFactor = Decimal.Parse(strdScaleFactor, System.Globalization.NumberStyles.Float);
I don't know if my answer to the question can still be helpful. But in this case I suggest the "decomposition of the double variable into decimal places" to store it in an Array / Array of data of type String.
This process of decomposition and storage in parts (number by number) from double to string, would basically work with the use of two loops and an "alternative" (if you thought of workaround, I think you got it), where the first loop will extract the values from double without converting to String, resulting in blessed scientific notation and storing number by number in an Array. And this will be done using MOD - the same method to check a palindrome number, which would be for example:
String[] Array_ = new double[ **here you will put an extreme value of places your DOUBLE can reach, you must have a prediction**];
for (int i = 0, variableDoubleMonstrous > 0, i++){
x = variableDoubleMonstrous %10;
Array_[i] = x;
variableDoubleMonstrous /= 10;
}
And the second loop to invert the Array values ​​(because in this process of checking a palindrome, the values ​​invert from the last place, to the first, from the penultimate to the second and so on. Remember?) to get the original value:
String[] ArrayFinal = new String[the same number of "places" / indices of the other Array / Data array];
int lengthArray = Array_.Length;
for (int i = 0, i < Array_.Length, i++){
FinalArray[i] = Array_[lengthArray - 1];
lengthArray--;
}
***Warning: There's a catch that I didn't pay attention to. In that case there will be no "." (floating point decimal separator or double), so this solution is not generalized. But if it is really important to use decimal separators, unfortunately the only possibility (If done well, it will have a great performance) is:
**Use a routine to get the position of the decimal point of the original value, the one with scientific notation - the important thing is that you know that this floating point is before a number such as the "Length" position x, and after a number such as the y position - extracting each digit using the loops - as shown above - and at the end "export" the data from the last Array to another one, including the decimal place divider (the comma, or the period , if variable decimal, double or float) in the imaginary position that was in the original variable, in the "real" position of that matrix.
*** The concept of position is, find out how many numbers occur before the decimal point, so with this information you will be able to store in the String Array the point in the real position.
NEEDS THAT CAN BE MADE:
But then you ask:
But what about when I'm going to convert String to a floating point value?
My answer is that you use the second matrix of this entire process (the one that receives the inversion of the first matrix that obtains the numbers by the palindrome method) and use it for the conversion, but always making sure, when necessary, of the position of the decimal place in future situations, in case this conversion (Double -> String) is needed again.
But what if the problem is to use the value of the converted Double (Array of Strings) in a calculation. Then in this case you went around in circles. Well, the original variable will work anyway even with scientific notation. The only difference between floating point and decimal variable types is in the rounding of values, which depending on the purpose, it will only be necessary to change the type of data used, but it is dangerous to have a significant loss of information, look here
I could be wrong, but isn't it like this?
data.ToString("n");
http://msdn.microsoft.com/en-us/library/dwhawy9k.aspx
i think you need only to use IFormat with
ToString(doubleVar, System.Globalization.NumberStyles.Number)
example:
double d = double.MaxValue;
string s = d.ToString(d, System.Globalization.NumberStyles.Number);
My solution was using the custom formats.
try this:
double d;
d = 1234.12341234;
d.ToString("#########0.#########");
Just to build on what jcasso said what you can do is to adjust your double value by changing the exponent so that your favorite format would do it for you, apply the format, and than pad the result with zeros to compensate for the adjustment.
This works fine for me...
double number = 1.5E+200;
string s = number.ToString("#");
//Output: "150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

Finding the largest possible number in permutations of digits less than a limit

I want to permute the digits of an int in a way that the result is the biggest possible permutation. This is easily done like this:
//how to deal with really large ints e.g.int32.MaxValue goes here
// in this case the algorithm would need to round down to the largest possible value
// but still needs to be less than int32.MaxValue
//This code will just handle normal values <int32.MaxValue
public static int Max(int number)
{
var numberAsCharArray = number.ToString().OrderByDescending(c => c).ToArray();
var largestNumberAsString = new string(numberAsCharArray);
return Int32.Parse(largestNumberAsString);
}
However, when the input has the same number of digits as Int32.MaxValue and contains at least one high digit, this digit will go to the first position making the result > Int32.MaxValue and leading to an exception when converting to int.
How could I limit the result to be <= Int32.MaxValue but within this limit still be the greatest possible permutation?
N.B. Negative numbers, e.g. -1234567890 are allowed as well as positive ones; in case of negative input, - sign should be dropped: -1234567890 should produce 2147398650 output
For small numbers (less or equal to 1000000000) you can do the business as usual; for numbers greater than one billion
you can try the follow approach:
Try follow int.MaxValue pattern (2147483647) as long as it's possible
When it's not possible, put the maximum number you can do and continue doing the business as usual for the remaining digits.
For instance, given 1234567890
1234567890 <- initial value
2147483647 <- int.MaxValue pattern
2147398650 <- solution
^
|
Here we can't put another 4, we put maximum available - 3
Remaining digits [56890] we order by descending - "98650" - business as usual
Implementation
private static int Biggest(int value) {
// Special MinValue case;
// we can't do Math.Abs() because of integer overflow
if (value == int.MinValue)
return 2147483486;
string st = value.ToString().Trim('-');
if (value <= 1000000000 && value >= -1000000000)
return int.Parse(string.Concat(st.OrderByDescending(c => c)));
string max = int.MaxValue.ToString();
List<int> digits = st.Select(c => c - '0').ToList();
StringBuilder sb = new StringBuilder(9);
bool exact = true;
while (digits.Any()) {
for (int i = 0; i < max.Length; ++i) {
int digitToFind = max[i] - '0';
int digitActual;
digitActual = digits
.Where(d => !exact || d <= digitToFind)
.OrderByDescending(d => d)
.First();
if (exact)
exact = digitActual == digitToFind;
sb.Append(digitActual);
digits.Remove(digitActual);
}
}
return int.Parse(sb.ToString());
}
Test:
// 2147398650 (for reference: int.MaxValue == 2147483647)
Console.WriteLine(Biggest(1234567890));
I suggest this:
int number = 587;
int maximum = Int32.MaxValue;
var result = "";
if(number == Int32.MinValue)
{
result = "2147483486";
}
else
{
// create list of available digits removing - sign from the string
var inputDigits = number.ToString().Replace("-", String.Empty).Select(c => Int32.Parse(new string(c, 1))).ToList();
var limitDigits = maximum.ToString().Select(c => Int32.Parse(new string(c, 1))).ToList();
var orderedDigits = inputDigits.OrderByDescending(c => c).ToList();
int position = 0;
// we only have to compare to the maximum if we have at least the same amount of digits in the input.
bool compareValues = limitDigits.Count <= inputDigits.Count;
// while we have not used all of the digits
while (orderedDigits.Count > 0)
{
// loop over the remaining digits from high to low values
for (int i = 0; i < orderedDigits.Count; i++)
{
// if it is above the digit in the maximum at the corresponding place we may only use it if input is shorter than maximum or if we have already used a lower value in a previous digit.
if (orderedDigits[i] > limitDigits[position])
{
if (compareValues)
{
continue;
}
}
else if (orderedDigits[i] < limitDigits[position])
{
// remember that we have already used a lower value
compareValues = false;
}
result += (orderedDigits[i].ToString());
orderedDigits.RemoveAt(i);
break;
}
position++;
}
}
var intResult = Int32.Parse(result);
EDIT:
inserted .Replace("-", String.Empty) when defining inputDigits to support negative numbers

Get fractional part of a number using IEEE754

I have a code that works using loop but it's bad imo, because can be simplified. I mean this loop:
private static long GetFract(double d)
{
if (d < 0)
return GetFract(-d);
d -= Math.Truncate(d);
long result = 0;
checked
{
while (true)
{
try
{
d *= 10;
long tmp = (long)d;
if (Math.Abs(tmp - d) < double.Epsilon)
return tmp;
result = tmp;
}
catch
{
return result;
}
}
}
}
expected results: 2.3333 -> 3333
so can we use IEEE754 to get a fractional part of a number without using ToString(), Split() and other functions, using just FP and integer math? I mean some bit magic:
private static unsafe long GetFract(double d)
{
if (d < 0)
return GetFract(-d);
d -= Math.Truncate(d);
const long mask = 0xFFFFFFFFFFFFF; //some magic const here
long x = *((long*) &d);
return x & mask;
}
we assume that d is always in [0..1], long and double are 8 bytes both
The sequence of bits representing the number 3333 is not found in the IEEE 754 representation of 2.3333, nor 0.3333, because IEEE 754 uses a binary exponent, not decimal.
That is, you are looking for the numerator in 3333 / 10000 but the internal representation is (when converted to decimal) 6004199023210345 / 18014398509481984 (that denominator is 254)
There's no bit hack that will extract data that isn't there in the first place.

Get a range from two integers

string num = db.SelectNums(id);
string[] numArr = num.Split('-').ToArray();
string num contain for an example "48030-48039";
string[] numArr will therefor contain (48030, 48039).
Now I have two elements, a high and low. I now need to get ALL the numbers from 48030 to 48039. The issue is that it has to be string since there will be telephone numbers with leading zeroes now and then.
Thats why I cannot use Enumerable.Range().ToArray() for this.
Any suggestions? The expected result should be 48030, 48031, 48032, ... , 48039
This should work with your leading zero requirement:
string num = db.SelectNums(id);
string[] split = num.Split('-');
long start = long.Parse(split[0]);
long end = long.Parse(split[1]);
bool includeLeadingZero = split[0].StartsWith("0");
List<string> results = new List<string>();
for(int i = start; i <= end; i++)
{
string result = includeLeadingZero ? "0" : "";
result += i.ToString();
results.Add(result);
}
string[] arrayResults = results.ToArray();
A few things to note:
It assumes your input will be 2 valid integers split by a single hyphen
I am giving you a string array result, personally I would prefer to work with a List<int> in the end
If the first number contains a single leading zero, then all results will contain a single leading zero
It uses long to cater for longer numbers, beware that the max number that will parse is 9,223,372,036,854,775,807, you could double this value (not length) by using ulong
Are you saying this?
int[] nums = new int[numArr.Length];
for (int i = 0; i < numArr.Length; i++)
{
nums[i] = Convert.ToInt32(numArr[i]);
}
Array.Sort(nums);
for (int n = nums[0]; n <= nums[nums.Length - 1]; n++)
{
Console.WriteLine(n);
}
here link
I am expecting your string always have valid two integers, so using Parse instead TryParse
string[] strList = "48030-48039".Split('-').ToArray();
var lst = strList.Select(int.Parse).ToList();
var min = lst.OrderBy(l => l).FirstOrDefault();
var max = lst.OrderByDescending(l => l).FirstOrDefault();
var diff = max - min;
//adding 1 here otherwise 48039 will not be there
var rng = Enumerable.Range(min,diff+1);
If you expecting invalid string
var num = 0;
var lst = (from s in strList where int.TryParse(s, out num) select num).ToList();
This is one way to do it:
public static string[] RangeTest()
{
Boolean leadingZero = false;
string num = "048030-48039"; //db.SelectNums(id);
if (num.StartsWith("0"))
leadingZero = true;
int min = int.Parse(num.Split('-').Min());
int count = int.Parse(num.Split('-').Max()) - min;
if (leadingZero)
return Enumerable.Range(min, count).Select(x => "0" + x.ToString()).ToArray();
else
return Enumerable.Range(min, count).Select(x => "" + x.ToString()).ToArray(); ;
}
You can use string.Format to ensure numbers are formatted with leading zeros. That will make the method work with arbitrary number of leading zeros.
private static string CreateRange(string num)
{
var tokens = num.Split('-').Select(s => s.Trim()).ToArray();
// use UInt64 to allow huge numbers
var start = UInt64.Parse(tokens[0]);
var end = UInt64.Parse(tokens[1]);
// if your start number is '000123', this will create
// a format string with 6 zeros ('000000')
var format = new string('0', tokens[0].Length);
// use StringBuilder to make GC happy.
// (if only there was a Enumerable.Range<ulong> overload...)
var sb = new StringBuilder();
for (var i = start; i <= end; i++)
{
// format ensures that your numbers are padded properly
sb.Append(i.ToString(format));
sb.Append(", ");
}
// trim trailing comma after the last element
if (sb.Length >= 2) sb.Length -= 2;
return sb.ToString();
}
Usage example:
// prints 0000012, 0000013, 0000014
Console.WriteLine( CreateRange("0000012-0000014") );
Three significant issues were brought up in comments:
The phone numbers have enough digits to exceed Int32.MaxValue so
converting to int isn't viable.
The phone numbers can have leading zeros (presumeably for some
international calling?)
The possible range of numbers can theoretically exceed the maximum size of an array (which may have memory issues, and I think may not be represented as a string)
As such, you may need to use long instead of int, and I would suggest using deferred execution if needed for very large ranges.
public static IEnumerable<string> EnumeratePhoneNumbers(string fromAndTo)
{
var split = fromAndTo.Split('-');
return EnumeratePhoneNumbers(split[0], split[1]);
}
public static IEnumerable<string> EnumeratePhoneNumbers(string fromString, string toString)
{
long from = long.Parse(fromString);
long to = long.Parse(toString);
int totalNumberLength = fromString.Length;
for (long phoneNumber = from; phoneNumber <= to; phoneNumber++)
{
yield return phoneNumber.ToString().PadLeft(totalNumberLength, '0');
}
}
This assumes that the padded zeros are already included in the lower bound fromString text. It will iterate and yield out numbers as you need them. This can be useful if you're churning out a lot of numbers and don't need to fill up memory with them, or if you just need the first 10 or 100. For example:
var first100Numbers =
EnumeratePhoneNumbers("0018155500-7018155510")
.Take(100)
.ToArray();
Normally that range would produce 7 billion results which cannot be stored in an array, and might run into memory issues (I'm not even sure if it can be stored in a string); by using deferred execution, you only create the 100 needed.
If you do have a small range, you can still join up your results into a string as you desired:
string numberRanges = String.Join(", ", EnumeratePhoneNumbers("0018155500-0018155510"));
And naturally you can put this array creation into your own helper method:
public static string GetPhoneNumbersListing(string fromAndTo)
{
return String.Join(", ", EnumeratePhoneNumbers("0018155500-0018155510"));
}
So your usage would be:
string numberRanges = GetPhoneNumbersListing("0018155500-0018155510");
A complete solution inspired by the answer by #Dan-o:
Inputs:
Start: 48030
End: 48039
Digits: 6
Expected String Output:
048030, 048031, 048032, 048033, 048034, 048035, 048036, 048037, 048038, 048039
Program:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class Program
{
public static void Main()
{
int first = 48030;
int last = 48039;
int digits = 6;
Console.WriteLine(CreateRange(first, last, digits));
}
public static string CreateRange(int first, int last, int numDigits)
{
string separator = ", ";
var sb = new StringBuilder();
sb.Append(first.ToString().PadLeft(numDigits, '0'));
foreach (int num in Enumerable.Range(first + 1, last - first))
{
sb.Append(separator + num.ToString().PadLeft(numDigits, '0'));
}
return sb.ToString();
}
}
For Each item In Enumerable.Range(min, count).ToArray()
something = item.PadLeft(5, "0")
Next

Categories

Resources