C# 无精度损失整数算法
如何创建一个使用整数的类型,至少支持加法、减法、除法和乘法,并在操作导致整数时保证和整数(否则抛出) 例如,我希望能够做到以下几点:C# 无精度损失整数算法,c#,math,floating-point,C#,Math,Floating Point,如何创建一个使用整数的类型,至少支持加法、减法、除法和乘法,并在操作导致整数时保证和整数(否则抛出) 例如,我希望能够做到以下几点: Precise A = 10; A.Divide(3); A.GetNumber(); // This would throw an exception as 10/3 isn't an int. A.Multiply(6); int result = A.GetNumber; // I want result to be = to 20, not to a fl
Precise A = 10;
A.Divide(3);
A.GetNumber(); // This would throw an exception as 10/3 isn't an int.
A.Multiply(6);
int result = A.GetNumber; // I want result to be = to 20, not to a floating point type that would round to 2 or be very close like 1.9999999999999999999999998992
我意识到这是一个奇怪的用例,但我确实有这样的需求(执行一系列操作,这些操作在浮点中可能会被错误表示,但最终保证为有效的整数)。然后使用Math.round()对最终答案进行取整。然后使用Math.round()对最终答案进行取整。如果不允许任意精度有理数,你似乎在问一个没有更多约束的不可能的问题
取1除以65537两次,再乘以65537两次,得到1:这不能用32位整数表示。如果你不允许任意精度有理数,那么你似乎在问没有更多约束的不可能
取1除以65537两次,然后再乘以65537两次,得到1:这不适合32位整数。因为我们不知道
10/3
最终会得到一个精确的整数答案,直到*6
之后,我们必须承诺推迟到那时:
public sealed class Precise
{
private interface IOperation
{
int Calculate(int value);
IOperation Combine(IOperation next);
}
private sealed class NoOp : IOperation
{
public static NoOp Instance = new NoOp();
public int Calculate(int value)
{
return value;
}
public IOperation Combine(IOperation next)
{
return next;
}
}
private sealed class Combo : IOperation
{
private readonly IOperation _first;
private readonly IOperation _second;
public Combo(IOperation first, IOperation second)
{
_first = first;
_second = second;
}
public int Calculate(int value)
{
return _second.Calculate(_first.Calculate(value));
}
public IOperation Combine(IOperation next)
{
return new Combo(_first, _second.Combine(next));
}
}
private sealed class Mult : IOperation
{
private readonly int _multiplicand;
public Mult(int multiplicand)
{
_multiplicand = multiplicand;
}
public int Calculate(int value)
{
return value * _multiplicand;
}
public int Multiplicand
{
get { return _multiplicand; }
}
public IOperation Combine(IOperation next)
{
var nextMult = next as Mult;
if(nextMult != null)
return new Mult(_multiplicand * nextMult._multiplicand);
var nextDiv = next as Div;
if(nextDiv != null)
{
int divisor = nextDiv.Divisor;
if(divisor == _multiplicand)
return NoOp.Instance;//multiplcation by 1
if(divisor > _multiplicand)
{
if(divisor % _multiplicand == 0)
return new Div(divisor / _multiplicand);
}
if(_multiplicand % divisor == 0)
return new Mult(_multiplicand / divisor);
}
return new Combo(this, next);
}
}
private sealed class Div : IOperation
{
private readonly int _divisor;
public Div(int divisor)
{
_divisor = divisor;
}
public int Divisor
{
get { return _divisor; }
}
public int Calculate(int value)
{
int ret = value / _divisor;
if(value != ret * _divisor)
throw new InvalidOperationException("Imprecise division");
return ret;
}
public IOperation Combine(IOperation next)
{
var nextDiv = next as Div;
if(nextDiv != null)
return new Div(_divisor * nextDiv._divisor);
var nextMult = next as Mult;
if(nextMult != null)
{
var multiplicand = nextMult.Multiplicand;
if(multiplicand == _divisor)
return NoOp.Instance;
if(multiplicand > _divisor)
{
if(multiplicand % _divisor == 0)
return new Mult(multiplicand / _divisor);
}
else if(_divisor % multiplicand == 0)
return new Div(multiplicand / _divisor);
}
return new Combo(this, next);
}
}
private sealed class Plus : IOperation
{
private readonly int _addend;
public Plus(int addend)
{
_addend = addend;
}
public int Calculate(int value)
{
return value + _addend;
}
public IOperation Combine(IOperation next)
{
var nextPlus = next as Plus;
if(nextPlus != null)
{
int newAdd = _addend + nextPlus._addend;
return newAdd == 0 ? (IOperation)NoOp.Instance : new Plus(newAdd);
}
return new Combo(this, next);
}
}
private readonly int _value;
private readonly IOperation _operation;
public static readonly Precise Zero = new Precise(0);
private Precise(int value, IOperation operation)
{
_value = value;
_operation = operation;
}
public Precise(int value)
: this(value, NoOp.Instance)
{
}
public int GetNumber()
{
return _operation.Calculate(_value);
}
public static explicit operator int(Precise value)
{
return value.GetNumber();
}
public static implicit operator Precise(int value)
{
return new Precise(value);
}
public override string ToString()
{
return GetNumber().ToString();
}
public Precise Multiply(int multiplicand)
{
if(multiplicand == 0)
return Zero;
return new Precise(_value, _operation.Combine(new Mult(multiplicand)));
}
public static Precise operator * (Precise precise, int value)
{
return precise.Multiply(value);
}
public Precise Divide(int divisor)
{
return new Precise(_value, _operation.Combine(new Div(divisor)));
}
public static Precise operator / (Precise precise, int value)
{
return precise.Divide(value);
}
public Precise Add(int addend)
{
return new Precise(_value, _operation.Combine(new Plus(addend)));
}
public Precise Subtract(int minuend)
{
return Add(-minuend);
}
public static Precise operator + (Precise precise, int value)
{
return precise.Add(value);
}
public static Precise operator - (Precise precise, int value)
{
return precise.Subtract(value);
}
}
这里每个Precise
都有一个整数值和一个将在其上执行的操作。进一步的操作通过一个新的操作产生一个新的精确的(将这种事情作为可变项来做是疯狂的),但是如果可能的话,这些操作被组合成一个简单的操作。因此,“除以三再乘以六”就变成了“乘以二”
我们可以这样测试:
public static void Main(string[] args)
{
Precise A = 10;
A /= 3;
try
{
var test = (int)A;
}
catch(InvalidOperationException)
{
Console.Error.WriteLine("Invalid operation attempted");
}
A *= 6;
int result = (int)A;
Console.WriteLine(result);
// Let's do 10 / 5 * 2 = 4 because it works but can't be pre-combined:
Console.WriteLine(new Precise(10) / 5 * 2);
// Let's do 10 / 5 * 2 - 6 + 4 == 2 to mix in addition and subtraction:
Console.WriteLine(new Precise(10) / 5 * 2 - 6 + 4);
Console.Read();
}
一个好的解决方案还可以很好地处理LHS为整数、RHS为精确
以及两者都为精确
的操作;留给读者作为练习;)
遗憾的是,我们必须更加复杂地处理(10/3+1)*3
,并且必须在组合
实现中进行改进
编辑:进一步思考一下如何做好上述工作,以至少捕获大多数边缘情况,我认为应该从只处理两个Precise
对象之间的操作开始,因为执行int
->Precise
非常简单,很容易被放在顶部,但是要精确计算,可能太早了,需要调用计算。我还将使操作成为操作的关键(让操作存储一个或两个对象,这些对象依次包含一个操作或一个值)。然后,如果你从一个和的表示形式开始,并将它乘以6,就更容易把它转换成(10*(6/3))+(5*6)
在最终计算时,它可以给出精确的结果50
,而不是失败,因为它击中了不精确的10/3
,,因为我们不知道10/3
最终会得到一个精确的整数答案,直到*6
之后,我们必须推迟到那时,并承诺:
public sealed class Precise
{
private interface IOperation
{
int Calculate(int value);
IOperation Combine(IOperation next);
}
private sealed class NoOp : IOperation
{
public static NoOp Instance = new NoOp();
public int Calculate(int value)
{
return value;
}
public IOperation Combine(IOperation next)
{
return next;
}
}
private sealed class Combo : IOperation
{
private readonly IOperation _first;
private readonly IOperation _second;
public Combo(IOperation first, IOperation second)
{
_first = first;
_second = second;
}
public int Calculate(int value)
{
return _second.Calculate(_first.Calculate(value));
}
public IOperation Combine(IOperation next)
{
return new Combo(_first, _second.Combine(next));
}
}
private sealed class Mult : IOperation
{
private readonly int _multiplicand;
public Mult(int multiplicand)
{
_multiplicand = multiplicand;
}
public int Calculate(int value)
{
return value * _multiplicand;
}
public int Multiplicand
{
get { return _multiplicand; }
}
public IOperation Combine(IOperation next)
{
var nextMult = next as Mult;
if(nextMult != null)
return new Mult(_multiplicand * nextMult._multiplicand);
var nextDiv = next as Div;
if(nextDiv != null)
{
int divisor = nextDiv.Divisor;
if(divisor == _multiplicand)
return NoOp.Instance;//multiplcation by 1
if(divisor > _multiplicand)
{
if(divisor % _multiplicand == 0)
return new Div(divisor / _multiplicand);
}
if(_multiplicand % divisor == 0)
return new Mult(_multiplicand / divisor);
}
return new Combo(this, next);
}
}
private sealed class Div : IOperation
{
private readonly int _divisor;
public Div(int divisor)
{
_divisor = divisor;
}
public int Divisor
{
get { return _divisor; }
}
public int Calculate(int value)
{
int ret = value / _divisor;
if(value != ret * _divisor)
throw new InvalidOperationException("Imprecise division");
return ret;
}
public IOperation Combine(IOperation next)
{
var nextDiv = next as Div;
if(nextDiv != null)
return new Div(_divisor * nextDiv._divisor);
var nextMult = next as Mult;
if(nextMult != null)
{
var multiplicand = nextMult.Multiplicand;
if(multiplicand == _divisor)
return NoOp.Instance;
if(multiplicand > _divisor)
{
if(multiplicand % _divisor == 0)
return new Mult(multiplicand / _divisor);
}
else if(_divisor % multiplicand == 0)
return new Div(multiplicand / _divisor);
}
return new Combo(this, next);
}
}
private sealed class Plus : IOperation
{
private readonly int _addend;
public Plus(int addend)
{
_addend = addend;
}
public int Calculate(int value)
{
return value + _addend;
}
public IOperation Combine(IOperation next)
{
var nextPlus = next as Plus;
if(nextPlus != null)
{
int newAdd = _addend + nextPlus._addend;
return newAdd == 0 ? (IOperation)NoOp.Instance : new Plus(newAdd);
}
return new Combo(this, next);
}
}
private readonly int _value;
private readonly IOperation _operation;
public static readonly Precise Zero = new Precise(0);
private Precise(int value, IOperation operation)
{
_value = value;
_operation = operation;
}
public Precise(int value)
: this(value, NoOp.Instance)
{
}
public int GetNumber()
{
return _operation.Calculate(_value);
}
public static explicit operator int(Precise value)
{
return value.GetNumber();
}
public static implicit operator Precise(int value)
{
return new Precise(value);
}
public override string ToString()
{
return GetNumber().ToString();
}
public Precise Multiply(int multiplicand)
{
if(multiplicand == 0)
return Zero;
return new Precise(_value, _operation.Combine(new Mult(multiplicand)));
}
public static Precise operator * (Precise precise, int value)
{
return precise.Multiply(value);
}
public Precise Divide(int divisor)
{
return new Precise(_value, _operation.Combine(new Div(divisor)));
}
public static Precise operator / (Precise precise, int value)
{
return precise.Divide(value);
}
public Precise Add(int addend)
{
return new Precise(_value, _operation.Combine(new Plus(addend)));
}
public Precise Subtract(int minuend)
{
return Add(-minuend);
}
public static Precise operator + (Precise precise, int value)
{
return precise.Add(value);
}
public static Precise operator - (Precise precise, int value)
{
return precise.Subtract(value);
}
}
这里每个Precise
都有一个整数值和一个将在其上执行的操作。进一步的操作通过一个新的操作产生一个新的精确的(将这种事情作为可变项来做是疯狂的),但是如果可能的话,这些操作被组合成一个简单的操作。因此,“除以三再乘以六”就变成了“乘以二”
我们可以这样测试:
public static void Main(string[] args)
{
Precise A = 10;
A /= 3;
try
{
var test = (int)A;
}
catch(InvalidOperationException)
{
Console.Error.WriteLine("Invalid operation attempted");
}
A *= 6;
int result = (int)A;
Console.WriteLine(result);
// Let's do 10 / 5 * 2 = 4 because it works but can't be pre-combined:
Console.WriteLine(new Precise(10) / 5 * 2);
// Let's do 10 / 5 * 2 - 6 + 4 == 2 to mix in addition and subtraction:
Console.WriteLine(new Precise(10) / 5 * 2 - 6 + 4);
Console.Read();
}
一个好的解决方案还可以很好地处理LHS为整数、RHS为精确
以及两者都为精确
的操作;留给读者作为练习;)
遗憾的是,我们必须更加复杂地处理(10/3+1)*3
,并且必须在组合
实现中进行改进
编辑:进一步思考一下如何做好上述工作,以至少捕获大多数边缘情况,我认为应该从只处理两个Precise
对象之间的操作开始,因为执行int
->Precise
非常简单,很容易被放在顶部,但是要精确计算,可能太早了,需要调用计算。我还将使操作成为操作的关键(让操作存储一个或两个对象,这些对象依次包含一个操作或一个值)。然后,如果你从一个和的表示形式开始,并将它乘以6,就更容易把它转换成(10*(6/3))+(5*6)
在最终计算时,它可以给出精确的结果50
,而不是失败,因为它击中了不精确的10/3
我会使用十进制作为运算结果,如果有“.”则在.ToString上的GetNumber检查中。如果是,我会抛出异常,如果没有,我将其转换为整数。我将使用十进制作为运算结果,如果有“.”,则在.ToString上的GetNumber检查中使用。如果有,则引发异常,如果没有,则将其转换为整数。为什么结果为2?我预计18或20岁。但是你可以做普通除法,然后投到一个int@Sayse通过我,但那Divide
是一个DivideInto
,所以它计算3/10。。。(命名很难)。不做推荐。如果你陷入困境,我们会帮助你找到一个“大型rational”库,但我们不会为你找到它。事实上,这只是一个打字错误,我在写完后没有更改我的评论(a最初是1)。现在更改文本。我不想像你写的那样存储任意精度的数字问题:在第一次除法之后,如何存储中间结果?为什么结果是2?我预计18或20岁。但是你可以做普通除法,然后投到一个int@Sayse通过我,但那是一个