【发布时间】:2021-11-23 14:11:25
【问题描述】:
我正在尝试将双精度数转换为数字数组
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”的问题(当数字太长时)。我不太喜欢 string 方法,因为它可能有意外的错误(例如 CultureInfo、1E+17、...)谁知道是否还有更多我不知道的情况 - 风险太大而我的应用程序没有使用字符串显示数字,结合精灵图像绘制数字。
所以我想试试数学方法:
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 没有列出我可能遇到的所有问题。
【问题讨论】:
-
能否添加样本输入数据和预期输出数据
-
@zaitsman:我添加了示例输入、输出数据。
-
您可以将其添加为任何实际数组。例如
var input = new [] {"whatever", "here"};var output = new int[] { 42 };或类似的。可以将 paste 直接复制到 c# 代码中的东西。 -
您的示例
-69.69777没有精确的双重表示。看这里:(-69.6977).ToString("N20")给出“-69,69769999999999754436”(另见stackoverflow.com/questions/588004/…)。所以一般来说,您必须将精度作为参数提供给函数。 -
至于数学方法和获取点后面的数字..当您反复除以 10 以得到点左侧的数字时,重新开始反复乘以 10 和修改把数字放在右边
标签: c# math numbers double floating-accuracy