将双数转换为数字,反之亦然?
Convert double number to digits and vice versa?
我正在尝试将双精度数转换为数字数组
Input:
double num
Output:
int[] arrDigit
int dotIdx
bool isMinus
例如:
Input:
double num = -69.69777
Output:
int[] arrDigit = { 7,7,7,9,6,9,6}
int dotIdx = 5
bool isMinus = true
反之亦然:
Input:
array of input digit commands
Output:
double num
例如:
Input:
Insert digit 6
Insert digit 9
Start dot
Insert digit 6
Insert digit 9
Insert digit 7
Insert digit 7
Insert digit 7
Output:
double num=69.69777
最简单的方法是使用C#字符串方法,我已经实现了:
class DigitToNumTranslator
{
private bool m_isDot;
//Minus is handled as operator, not the job for translator
//Helper
private StringBuilder m_builder = new StringBuilder();
public double NumResult
{
get
{
return double.Parse(m_builder.ToString(), System.Globalization.CultureInfo.InvariantCulture);
}
}
public void Reset()
{
m_builder.Clear();
m_isDot = false;
}
public void StartDot()
{
if (!m_isDot)
{
m_isDot = true;
m_builder.Append('.');
}
}
public void InsertDigit(int digit)
{
m_builder.Append(digit.ToString());
}
}
class NumToDigitTranslator
{
private List<int> m_lstDigit;
private IList<int> m_lstDigitReadOnly;
private int m_dotIdx;
private bool m_isMinus;
public IList<int> LstDigit => m_lstDigitReadOnly;
public int DotIdx => m_dotIdx;
public bool IsMinus => m_isMinus;
public NumToDigitTranslator()
{
m_lstDigit = new List<int>();
m_lstDigitReadOnly = m_lstDigit.AsReadOnly();
}
public void Translate(double num)
{
m_lstDigit.Clear();
m_dotIdx = 0;
m_isMinus = false;
var szNum = num.ToString(System.Globalization.CultureInfo.InvariantCulture);
//Won't work if it's 1E+17
for (var i = 0; i < szNum.Length; ++i)
{
if (char.IsNumber(szNum[i]))
m_lstDigit.Add(int.Parse(szNum[i].ToString()));
else if (szNum[i] == '-')
m_isMinus = true;
else if (szNum[i] == '.')
m_dotIdx = i;
}
//Reverse for display
if (m_dotIdx != 0)
m_dotIdx = szNum.Length - 1 - m_dotIdx;
m_lstDigit.Reverse();
}
}
但是字符串方法遇到了问题“1E+17”(当数字太长时)。我不太喜欢字符串方法,因为它可能有意想不到的错误(例如 CultureInfo,1E+17,...)谁知道是否还有更多我不知道的情况 - 风险太大而且我的应用程序没有使用字符串显示数字,结合sprite图像绘制数字。
所以我想试试数学方法:
class DigitToNumTranslatorRaw
{
private double m_numResult;
private bool m_isDot;
private int m_dotIdx;
public double NumResult => m_numResult;
public void Reset()
{
m_numResult = 0;
m_dotIdx = 1;
m_isDot = false;
}
public void StartDot()
{
m_isDot = true;
}
public void InsertDigit(int digit)
{
if (m_isDot)
{
m_numResult += digit * Math.Pow(10, -m_dotIdx);
++m_dotIdx;
}
else
{
m_numResult *= 10;
m_numResult += digit;
}
}
}
class NumToDigitTranslatorRaw
{
private List<int> m_lstDigit;
private IList<int> m_lstDigitReadOnly;
private int m_dotIdx;
public IList<int> LstDigit => m_lstDigitReadOnly;
public int DotIdx => m_dotIdx;
public NumToDigitTranslatorRaw()
{
m_lstDigit = new List<int>();
m_lstDigitReadOnly = m_lstDigit.AsReadOnly();
}
public void Translate(double num)
{
m_dotIdx = 0;
m_lstDigit.Clear();
//WIP (work with int, but not with double, thus failed to get the numbers after dot)
var intNum = (int)num;
while (num > 10)
{
m_lstDigit.Add((intNum % 10));
num /= 10;
}
if (m_lstDigit.Count > 0)
m_lstDigit.Reverse();
else
m_lstDigit.Add(0);
}
}
但是我遇到了两个问题:
在 DigitToNumTranslatorRaw
中,我现在不知道它是否比字符串解决方案更好。 m_numResult += digit * Math.Pow(10, -m_dotIdx);
, num /= 10;
,... 可能会导致浮点精度问题,而 Pow 是提高性能的最佳方式吗?
在NumToDigitTranslatorRaw
中,我仍然无法获取点后的数字。
我试图提取 code TryParse of Mircosoft 看看他们是怎么做的,但是它太复杂了,我找不到他们把代码放在哪里。
所以我的目的是:
数学方法:编写 DigitToNumTranslatorRaw
& NumToDigitTranslatorRaw
并确保它没有错误且浮点数准确且性能优于字符串方法(因为我不处理 CultureInfo.InvariantCulture, 1E+17,...).
如果数学方法太难,我就用字符串方法DigitToNumTranslator
& NumToDigitTranslator
来处理每个字符串问题(例如太长的数字变成1E+17), 但问题是我不知道我是否覆盖了所有字符串问题(例如我随机测试发现的 1E+17 ,我通过堆栈溢出搜索发现的 CultureInfo 问题),the docs 没有列出我可能遇到的所有问题。
代码使用示例:
数字转数字:
private DigitToNumTranslator m_digit = new DigitToNumTranslator();
m_digit.Reset();
var isEnd = false;
//m_lstInputKey is a list of enum E_INPUT_KEY, created earlier by user input
for (; i < m_lstInputKey.Count; ++i)
{
switch (m_lstInputKey[i])
{
case E_INPUT_KEY.NUM_0: m_digit.InsertDigit(0); break;
case E_INPUT_KEY.NUM_1: m_digit.InsertDigit(1); break;
case E_INPUT_KEY.NUM_2: m_digit.InsertDigit(2); break;
case E_INPUT_KEY.NUM_3: m_digit.InsertDigit(3); break;
case E_INPUT_KEY.NUM_4: m_digit.InsertDigit(4); break;
case E_INPUT_KEY.NUM_5: m_digit.InsertDigit(5); break;
case E_INPUT_KEY.NUM_6: m_digit.InsertDigit(6); break;
case E_INPUT_KEY.NUM_7: m_digit.InsertDigit(7); break;
case E_INPUT_KEY.NUM_8: m_digit.InsertDigit(8); break;
case E_INPUT_KEY.NUM_9: m_digit.InsertDigit(9); break;
case E_INPUT_KEY.NUM_DOT: m_digit.StartDot(); break;
default: isEnd = true; break;
}
if (isEnd) break;
}
Console.WriteLine(m_digit.NumResult);
数字到数字:
private NumToDigitTranslator m_numToDigitTranslator = new NumToDigitTranslator();
double dInputNumber = 6969696969696969696996.69696969696969D;
m_numToDigitTranslator.Translate(dInputNumber);
//Draw function is how you draw the information to the screen
DrawListDigit(m_numToDigitTranslator.LstDigit);
DrawMinus(m_numToDigitTranslator.IsMinus);
DrawDot(m_numToDigitTranslator.DotIdx);
数学解法
代码:
#region MATH_WAY
class DigitToNumTranslatorMath
{
private double m_numResult;
private bool m_isDot;
private int m_dotIdx;
public double NumResult => m_numResult;
public void Reset()
{
m_numResult = 0;
m_dotIdx = 1;
m_isDot = false;
}
public void StartDot()
{
m_isDot = true;
}
public void InsertDigit(int digit)
{
if (m_isDot)
{
m_numResult += digit * Math.Pow(10, -m_dotIdx);
++m_dotIdx;
}
else
{
m_numResult *= 10;
m_numResult += digit;
}
}
}
//Bug: (num - Math.Truncate(num))
//==> floating point problem
//==> 1.9D - Math.Truncate(1.9D) = 0.89999999999999991 (Expected: 0.9)
class NumToDigitTranslatorMath
{
private List<int> m_lstDigit;
private IList<int> m_lstDigitReadOnly;
private int m_dotIdx;
private bool m_isMinus;
public IList<int> LstDigit => m_lstDigitReadOnly;
public int DotIdx => m_dotIdx;
public bool IsMinus => m_isMinus;
public NumToDigitTranslatorMath()
{
m_lstDigit = new List<int>();
m_lstDigitReadOnly = m_lstDigit.AsReadOnly();
}
public void Translate(double num)
{
m_dotIdx = 0;
m_lstDigit.Clear();
m_isMinus = num < 0;
int intDigit;
double intNum;//Use double type to prevent casting a too big double for int which causes overflow
//Get the digits on the right of dot
const int NUM_COUNT_AFTER_DOT = 1000000000;//double has Precision 15-16 digits, but I only need 9 digits
//Math.Truncate(-1.9)=>-1; Math.Floor(-1.9)=>-2;
intNum = Math.Truncate((num - Math.Truncate(num)) * NUM_COUNT_AFTER_DOT);//Floating point bug here!!!
//Remove zeros
while (intNum > 0)
{
intDigit = (int)(intNum % 10);
if (intDigit != 0)
break;
else
intNum = Math.Truncate(intNum / 10);
}
while (intNum > 0)
{
intDigit = (int)(intNum % 10);
intNum = Math.Truncate(intNum / 10);
m_lstDigit.Add(intDigit);
++m_dotIdx;
}
//Get the digits on the left of dot
intNum = Math.Truncate(num);
while (intNum > 0)
{
intDigit = (int)(intNum % 10);
intNum = Math.Truncate(intNum / 10);
m_lstDigit.Add(intDigit);
}
if (m_lstDigit.Count == 0)
m_lstDigit.Add(0);
}
}
#endregion
注:存在浮点数问题,例如:1.9D - Math.Truncate(1.9D) = 0.89999999999999991 (预期:0.9).
我本来打算从 .Net source code 中提取代码以数学方式实现它,但我太懒了,所以我只使用字符串解决方案。
字符串解:
代码:
static class CONST_STR_FORMAT
{
private static System.Globalization.CultureInfo s_ciCommon = System.Globalization.CultureInfo.InvariantCulture;
public static System.Globalization.CultureInfo CI_COMMON => s_ciCommon;
//source:
public const string FORMAT_DOUBLE = "0.###################################################################################################################################################################################################################################################################################################################################################";
}
class DigitToNumTranslator
{
private bool m_isDot;
//Minus is handled as operator, not the job for translator
//Helper
private StringBuilder m_builder = new StringBuilder();
public double NumResult
{
get
{
return double.Parse(m_builder.ToString(), CONST_STR_FORMAT.CI_COMMON);
}
}
public void Reset()
{
m_builder.Clear();
m_isDot = false;
}
public void StartDot()
{
if (!m_isDot)
{
m_isDot = true;
m_builder.Append('.');
}
}
public void InsertDigit(int digit)
{
m_builder.Append(digit);
}
}
class NumToDigitTranslator
{
private List<int> m_lstDigit;
private IList<int> m_lstDigitReadOnly;
private int m_dotIdx;
private bool m_isMinus;
public IList<int> LstDigit => m_lstDigitReadOnly;
public int DotIdx => m_dotIdx;
public bool IsMinus => m_isMinus;
public NumToDigitTranslator()
{
m_lstDigit = new List<int>();
m_lstDigitReadOnly = m_lstDigit.AsReadOnly();
}
public void Translate(double num)
{
m_lstDigit.Clear();
m_dotIdx = 0;
m_isMinus = false;
var szNum = num.ToString(CONST_STR_FORMAT.FORMAT_DOUBLE, CONST_STR_FORMAT.CI_COMMON);
for (var i = 0; i < szNum.Length; ++i)
{
if (char.IsNumber(szNum[i]))
m_lstDigit.Add(int.Parse(szNum[i].ToString()));
else if (szNum[i] == '-')
m_isMinus = true;
else if (szNum[i] == '.')
m_dotIdx = i;
}
//Reverse for display
if (m_dotIdx != 0)
m_dotIdx = szNum.Length - 1 - m_dotIdx;
m_lstDigit.Reverse();
}
}
注意:不再头痛。我最担心的是文化错误(错误发生在某些设备上但我的设备上没有),希望代码 System.Globalization.CultureInfo.InvariantCulture
将确保噩梦不会发生。
我正在尝试将双精度数转换为数字数组
Input:
double num
Output:
int[] arrDigit
int dotIdx
bool isMinus
例如:
Input:
double num = -69.69777
Output:
int[] arrDigit = { 7,7,7,9,6,9,6}
int dotIdx = 5
bool isMinus = true
反之亦然:
Input:
array of input digit commands
Output:
double num
例如:
Input:
Insert digit 6
Insert digit 9
Start dot
Insert digit 6
Insert digit 9
Insert digit 7
Insert digit 7
Insert digit 7
Output:
double num=69.69777
最简单的方法是使用C#字符串方法,我已经实现了:
class DigitToNumTranslator
{
private bool m_isDot;
//Minus is handled as operator, not the job for translator
//Helper
private StringBuilder m_builder = new StringBuilder();
public double NumResult
{
get
{
return double.Parse(m_builder.ToString(), System.Globalization.CultureInfo.InvariantCulture);
}
}
public void Reset()
{
m_builder.Clear();
m_isDot = false;
}
public void StartDot()
{
if (!m_isDot)
{
m_isDot = true;
m_builder.Append('.');
}
}
public void InsertDigit(int digit)
{
m_builder.Append(digit.ToString());
}
}
class NumToDigitTranslator
{
private List<int> m_lstDigit;
private IList<int> m_lstDigitReadOnly;
private int m_dotIdx;
private bool m_isMinus;
public IList<int> LstDigit => m_lstDigitReadOnly;
public int DotIdx => m_dotIdx;
public bool IsMinus => m_isMinus;
public NumToDigitTranslator()
{
m_lstDigit = new List<int>();
m_lstDigitReadOnly = m_lstDigit.AsReadOnly();
}
public void Translate(double num)
{
m_lstDigit.Clear();
m_dotIdx = 0;
m_isMinus = false;
var szNum = num.ToString(System.Globalization.CultureInfo.InvariantCulture);
//Won't work if it's 1E+17
for (var i = 0; i < szNum.Length; ++i)
{
if (char.IsNumber(szNum[i]))
m_lstDigit.Add(int.Parse(szNum[i].ToString()));
else if (szNum[i] == '-')
m_isMinus = true;
else if (szNum[i] == '.')
m_dotIdx = i;
}
//Reverse for display
if (m_dotIdx != 0)
m_dotIdx = szNum.Length - 1 - m_dotIdx;
m_lstDigit.Reverse();
}
}
但是字符串方法遇到了问题“1E+17”(当数字太长时)。我不太喜欢字符串方法,因为它可能有意想不到的错误(例如 CultureInfo,1E+17,...)谁知道是否还有更多我不知道的情况 - 风险太大而且我的应用程序没有使用字符串显示数字,结合sprite图像绘制数字。
所以我想试试数学方法:
class DigitToNumTranslatorRaw
{
private double m_numResult;
private bool m_isDot;
private int m_dotIdx;
public double NumResult => m_numResult;
public void Reset()
{
m_numResult = 0;
m_dotIdx = 1;
m_isDot = false;
}
public void StartDot()
{
m_isDot = true;
}
public void InsertDigit(int digit)
{
if (m_isDot)
{
m_numResult += digit * Math.Pow(10, -m_dotIdx);
++m_dotIdx;
}
else
{
m_numResult *= 10;
m_numResult += digit;
}
}
}
class NumToDigitTranslatorRaw
{
private List<int> m_lstDigit;
private IList<int> m_lstDigitReadOnly;
private int m_dotIdx;
public IList<int> LstDigit => m_lstDigitReadOnly;
public int DotIdx => m_dotIdx;
public NumToDigitTranslatorRaw()
{
m_lstDigit = new List<int>();
m_lstDigitReadOnly = m_lstDigit.AsReadOnly();
}
public void Translate(double num)
{
m_dotIdx = 0;
m_lstDigit.Clear();
//WIP (work with int, but not with double, thus failed to get the numbers after dot)
var intNum = (int)num;
while (num > 10)
{
m_lstDigit.Add((intNum % 10));
num /= 10;
}
if (m_lstDigit.Count > 0)
m_lstDigit.Reverse();
else
m_lstDigit.Add(0);
}
}
但是我遇到了两个问题:
在
DigitToNumTranslatorRaw
中,我现在不知道它是否比字符串解决方案更好。m_numResult += digit * Math.Pow(10, -m_dotIdx);
,num /= 10;
,... 可能会导致浮点精度问题,而 Pow 是提高性能的最佳方式吗?在
NumToDigitTranslatorRaw
中,我仍然无法获取点后的数字。
我试图提取 code TryParse of Mircosoft 看看他们是怎么做的,但是它太复杂了,我找不到他们把代码放在哪里。
所以我的目的是:
数学方法:编写
DigitToNumTranslatorRaw
&NumToDigitTranslatorRaw
并确保它没有错误且浮点数准确且性能优于字符串方法(因为我不处理 CultureInfo.InvariantCulture, 1E+17,...).如果数学方法太难,我就用字符串方法
DigitToNumTranslator
&NumToDigitTranslator
来处理每个字符串问题(例如太长的数字变成1E+17), 但问题是我不知道我是否覆盖了所有字符串问题(例如我随机测试发现的 1E+17 ,我通过堆栈溢出搜索发现的 CultureInfo 问题),the docs 没有列出我可能遇到的所有问题。
代码使用示例:
数字转数字:
private DigitToNumTranslator m_digit = new DigitToNumTranslator();
m_digit.Reset();
var isEnd = false;
//m_lstInputKey is a list of enum E_INPUT_KEY, created earlier by user input
for (; i < m_lstInputKey.Count; ++i)
{
switch (m_lstInputKey[i])
{
case E_INPUT_KEY.NUM_0: m_digit.InsertDigit(0); break;
case E_INPUT_KEY.NUM_1: m_digit.InsertDigit(1); break;
case E_INPUT_KEY.NUM_2: m_digit.InsertDigit(2); break;
case E_INPUT_KEY.NUM_3: m_digit.InsertDigit(3); break;
case E_INPUT_KEY.NUM_4: m_digit.InsertDigit(4); break;
case E_INPUT_KEY.NUM_5: m_digit.InsertDigit(5); break;
case E_INPUT_KEY.NUM_6: m_digit.InsertDigit(6); break;
case E_INPUT_KEY.NUM_7: m_digit.InsertDigit(7); break;
case E_INPUT_KEY.NUM_8: m_digit.InsertDigit(8); break;
case E_INPUT_KEY.NUM_9: m_digit.InsertDigit(9); break;
case E_INPUT_KEY.NUM_DOT: m_digit.StartDot(); break;
default: isEnd = true; break;
}
if (isEnd) break;
}
Console.WriteLine(m_digit.NumResult);
数字到数字:
private NumToDigitTranslator m_numToDigitTranslator = new NumToDigitTranslator();
double dInputNumber = 6969696969696969696996.69696969696969D;
m_numToDigitTranslator.Translate(dInputNumber);
//Draw function is how you draw the information to the screen
DrawListDigit(m_numToDigitTranslator.LstDigit);
DrawMinus(m_numToDigitTranslator.IsMinus);
DrawDot(m_numToDigitTranslator.DotIdx);
数学解法
代码:
#region MATH_WAY
class DigitToNumTranslatorMath
{
private double m_numResult;
private bool m_isDot;
private int m_dotIdx;
public double NumResult => m_numResult;
public void Reset()
{
m_numResult = 0;
m_dotIdx = 1;
m_isDot = false;
}
public void StartDot()
{
m_isDot = true;
}
public void InsertDigit(int digit)
{
if (m_isDot)
{
m_numResult += digit * Math.Pow(10, -m_dotIdx);
++m_dotIdx;
}
else
{
m_numResult *= 10;
m_numResult += digit;
}
}
}
//Bug: (num - Math.Truncate(num))
//==> floating point problem
//==> 1.9D - Math.Truncate(1.9D) = 0.89999999999999991 (Expected: 0.9)
class NumToDigitTranslatorMath
{
private List<int> m_lstDigit;
private IList<int> m_lstDigitReadOnly;
private int m_dotIdx;
private bool m_isMinus;
public IList<int> LstDigit => m_lstDigitReadOnly;
public int DotIdx => m_dotIdx;
public bool IsMinus => m_isMinus;
public NumToDigitTranslatorMath()
{
m_lstDigit = new List<int>();
m_lstDigitReadOnly = m_lstDigit.AsReadOnly();
}
public void Translate(double num)
{
m_dotIdx = 0;
m_lstDigit.Clear();
m_isMinus = num < 0;
int intDigit;
double intNum;//Use double type to prevent casting a too big double for int which causes overflow
//Get the digits on the right of dot
const int NUM_COUNT_AFTER_DOT = 1000000000;//double has Precision 15-16 digits, but I only need 9 digits
//Math.Truncate(-1.9)=>-1; Math.Floor(-1.9)=>-2;
intNum = Math.Truncate((num - Math.Truncate(num)) * NUM_COUNT_AFTER_DOT);//Floating point bug here!!!
//Remove zeros
while (intNum > 0)
{
intDigit = (int)(intNum % 10);
if (intDigit != 0)
break;
else
intNum = Math.Truncate(intNum / 10);
}
while (intNum > 0)
{
intDigit = (int)(intNum % 10);
intNum = Math.Truncate(intNum / 10);
m_lstDigit.Add(intDigit);
++m_dotIdx;
}
//Get the digits on the left of dot
intNum = Math.Truncate(num);
while (intNum > 0)
{
intDigit = (int)(intNum % 10);
intNum = Math.Truncate(intNum / 10);
m_lstDigit.Add(intDigit);
}
if (m_lstDigit.Count == 0)
m_lstDigit.Add(0);
}
}
#endregion
注:存在浮点数问题,例如:1.9D - Math.Truncate(1.9D) = 0.89999999999999991 (预期:0.9).
我本来打算从 .Net source code 中提取代码以数学方式实现它,但我太懒了,所以我只使用字符串解决方案。
字符串解:
代码:
static class CONST_STR_FORMAT
{
private static System.Globalization.CultureInfo s_ciCommon = System.Globalization.CultureInfo.InvariantCulture;
public static System.Globalization.CultureInfo CI_COMMON => s_ciCommon;
//source:
public const string FORMAT_DOUBLE = "0.###################################################################################################################################################################################################################################################################################################################################################";
}
class DigitToNumTranslator
{
private bool m_isDot;
//Minus is handled as operator, not the job for translator
//Helper
private StringBuilder m_builder = new StringBuilder();
public double NumResult
{
get
{
return double.Parse(m_builder.ToString(), CONST_STR_FORMAT.CI_COMMON);
}
}
public void Reset()
{
m_builder.Clear();
m_isDot = false;
}
public void StartDot()
{
if (!m_isDot)
{
m_isDot = true;
m_builder.Append('.');
}
}
public void InsertDigit(int digit)
{
m_builder.Append(digit);
}
}
class NumToDigitTranslator
{
private List<int> m_lstDigit;
private IList<int> m_lstDigitReadOnly;
private int m_dotIdx;
private bool m_isMinus;
public IList<int> LstDigit => m_lstDigitReadOnly;
public int DotIdx => m_dotIdx;
public bool IsMinus => m_isMinus;
public NumToDigitTranslator()
{
m_lstDigit = new List<int>();
m_lstDigitReadOnly = m_lstDigit.AsReadOnly();
}
public void Translate(double num)
{
m_lstDigit.Clear();
m_dotIdx = 0;
m_isMinus = false;
var szNum = num.ToString(CONST_STR_FORMAT.FORMAT_DOUBLE, CONST_STR_FORMAT.CI_COMMON);
for (var i = 0; i < szNum.Length; ++i)
{
if (char.IsNumber(szNum[i]))
m_lstDigit.Add(int.Parse(szNum[i].ToString()));
else if (szNum[i] == '-')
m_isMinus = true;
else if (szNum[i] == '.')
m_dotIdx = i;
}
//Reverse for display
if (m_dotIdx != 0)
m_dotIdx = szNum.Length - 1 - m_dotIdx;
m_lstDigit.Reverse();
}
}
注意:不再头痛。我最担心的是文化错误(错误发生在某些设备上但我的设备上没有),希望代码 System.Globalization.CultureInfo.InvariantCulture
将确保噩梦不会发生。