虽然我的问题听起来微不足道,但实际上并非如此。希望你能帮助我。
我想在我的.NET (C#)项目中实现区间算术。这意味着每个数字都由一个下界和一个上界定义。这对解决像这样的问题很有帮助
1 / 3 = 0.333333333333333 (15 significant digits)因为这样你就会有
1 / 3 = [ 0.33333333333333 , 0.333333333333334 ] (14 significant digits each),所以我现在确信正确的答案就在这两个数字之间。如果没有区间表示,我就已经有了舍入误差(即0.0000000000000003)。
向下舍入结果将给出区间的下限,向上舍入结果将给出区间的上限。
.NET具有Math.Round(double,int)方法,该方法将double舍入到int个小数位。看起来不错,但不能强制向上/向下舍入。Math.Round(1.0/3.0,14)会向下舍入,但也需要向上舍入到0.33...34不能像这样实现。
但是你可能会说有Math.Ceil和Math.Floor!好的,这些方法舍入到下一个较低或较高的整数。因此,如果我想四舍五入到小数点后14位,我首先需要修改我的结果:
1 / 3 = 0.333333333333333 -> *E14 -> 33333333333333.3因此,现在我可以调用Math.Ceil和Math.Floor,并在重新调整后获得这两个四舍五入的结果
33333333333333 & 33333333333334 -> /E14 -> 0.33333333333333 & 0.33333333333334看起来不错,但是:假设我的数字接近double.MaxValue。我不能只是在double.MaxValue附近*E14一个值,因为这会给我一个OverflowException。因此,这也不是解决方案。
最重要的是:当尝试对0.9999999999999999999999999 (more than 15 digits)进行舍入时,所有这些都会失败,因为在我可以开始向下舍入之前,内部表示已经舍入为1。
我可以尝试以某种方式解析包含双精度值的字符串,但这不会有帮助,因为(1/3 * 3).ToString()将打印1而不是0.99...9。
Decimal也不能工作,因为我不想要那么深的精度,14位就足够了;但我仍然想要那个双倍范围!
在存在多个区间算术实现的C++中,这个问题可以通过动态地告诉处理器将其循环模式切换为例如"always down“或"always up”来解决。我在.NET中找不到任何方法来做这件事。
那么,你有什么想法吗?提前感谢!
发布于 2017-01-26 00:08:57
假设nextDown(x)是一个返回小于x的最大双精度数的函数,nextUp(x)是一个返回大于x的最小双精度数的函数。有关实现思想,请参阅Get next smallest Double number。
其中,您将向下舍入下限结果,而不是使用舍入到最近的结果的nextDown。如果要向上舍入一个上界,请使用舍入到最近的结果的nextUp。
此方法可确保间隔继续包含精确的实数结果。它引入了额外的舍入误差-在某些情况下,下限将比它应该的值小一个ULP,和/或上限将比它大一个ULP。但是,这是对间隔的最小加宽,比在十进制或通过抑制低有效位得到的加宽要小得多。
发布于 2017-01-25 21:15:39
这可能更像是一条长长的评论,而不是一个真正的答案。
这段代码通过截断7个最低有效位返回一个" Interval“(我只使用Tuple<,>,您可以使用自己的Interval类型):
static Tuple<double, double> GetMinMaxIntervalBasedOnBinaryNumbersThatAreRoundOnLastSevenBits(double number)
{
if (double.IsInfinity(number) || double.IsNaN(number))
return Tuple.Create(number, number); // maybe treat this case differently
var i = BitConverter.DoubleToInt64Bits(number);
const int numberOfBitsToClear = 7; // your seven, can change this value, must be below 52
const long precision = 1L << numberOfBitsToClear;
const long bitMask = ~(precision - 1L);
//truncate i
i &= bitMask;
return Tuple.Create(BitConverter.Int64BitsToDouble(i), BitConverter.Int64BitsToDouble(i + precision));
}免责声明:我不确定这对任何目的是否有用。特别是不确定它对于区间算术是否有用。
使用此代码,GetMinMaxIntervalBasedOnBinaryNumbersThatAreRoundOnLastSevenBits(1.0 / 3.0)将返回元组(0.333333333333329, 0.333333333333336)。
这段代码,就像您在问题中询问的代码一样,有一个明显的“问题”:如果原始值接近(甚至等于)我们使用的一个“四舍五入”数字,那么返回的区间是“倾斜的”,原始数字接近区间的一个末端。例如,在输入42.0 (已经是圆形)的情况下,您可以得到元组(42, 42.0000000000009)。
这段代码的一个好处是我希望它非常快。
https://stackoverflow.com/questions/41846690
复制相似问题