向给定数字舍入
有人可以提供将以 5 结尾的数字向给定数字舍入的算法的代码(任何语言都可以,但我编写的是 .Net 语言和 VB6)?
RoundTo(双精度值, 双精度 toWards, int numberOfDigitsBehindComma)
RoundTo(1.25,1,1)=1.2 RoundTo(1.25,2,1)=1.3
RoundTo(1.26,1,1)=1.3 RoundTo(1.24,2,1)=1.2
请提供负数的解决方案。
编辑:对于我的要求似乎有很多困惑,我将填写结果代码必须满足的所有断言。我的解决方案就是这样做的。
[TestMethod]
public void RoundTowards()
{
double x=3.44;double y=3.45;double z=4.45;
double a = 3.51; double b = 4.5001; double c = -1.14; double d = -1.15;
var mean=4;
Assert.AreEqual(3.4,x.RoundTowards(mean,1));
Assert.AreEqual(3.5, y.RoundTowards(mean, 1));
Assert.AreEqual(4.4, z.RoundTowards(mean, 1));
Assert.AreEqual(3.5, a.RoundTowards(mean, 1));
Assert.AreEqual(4.5, b.RoundTowards(mean, 1));
mean = 5;
Assert.AreEqual(3.4, x.RoundTowards(mean, 1));
Assert.AreEqual(3.5, y.RoundTowards(mean, 1));
Assert.AreEqual(4.5, z.RoundTowards(mean, 1));
Assert.AreEqual(3.5, a.RoundTowards(mean, 1));
Assert.AreEqual(4.5, b.RoundTowards(mean, 1));
mean = 3;
Assert.AreEqual(3.4, x.RoundTowards(mean, 1));
Assert.AreEqual(3.4, y.RoundTowards(mean, 1));
Assert.AreEqual(4.4, z.RoundTowards(mean, 1));
Assert.AreEqual(3.5, a.RoundTowards(mean, 1));
Assert.AreEqual(4.5, b.RoundTowards(mean, 1));
Assert.AreEqual(Math.Round(-1.1,4),Math.Round( c.RoundTowards(mean, 1),4));
Assert.AreEqual(Math.Round(-1.1,4),Math.Round(d.RoundTowards(mean, 1),4));
mean = -2;
Assert.AreEqual(Math.Round(3.4,4),Math.Round( x.RoundTowards(mean, 1),4));
Assert.AreEqual(Math.Round(3.4,4),Math.Round( y.RoundTowards(mean, 1),4));
Assert.AreEqual(Math.Round(4.4,4),Math.Round( z.RoundTowards(mean, 1),4));
Assert.AreEqual(Math.Round(3.5,4),Math.Round( a.RoundTowards(mean, 1),4));
Assert.AreEqual(Math.Round(4.5,4),Math.Round( b.RoundTowards(mean, 1),4));
Assert.AreEqual(Math.Round(-1.1, 4), Math.Round(c.RoundTowards(mean, 1), 4));
Assert.AreEqual(Math.Round(-1.2, 4), Math.Round(d.RoundTowards(mean, 1), 4));
}
[TestMethod]
public void RoundTowardsTowardZero()
{
double x = 3.45; double y = -3.45;
double a = -3.551; double b = 4.551; double c = 4.5500001; double d = 4.5501;
var mean = 0;
Assert.AreEqual(3.4, x.RoundTowards(mean, 1));
Assert.AreEqual(-3.4, y.RoundTowards(mean, 1));
Assert.AreEqual(-3.6, a.RoundTowards(mean, 1));
Assert.AreEqual(4.6, b.RoundTowards(mean, 1));
Assert.AreEqual(4.5, c.RoundTowards(mean, 1));
Assert.AreEqual(4.6, d.RoundTowards(mean, 1));
}
[TestMethod]
public void Test14_55()
{
Assert.AreEqual((14.55).RoundTowards(9, 1) ,14.5);
Assert.AreEqual((14.55).RoundTowards(15,1), 14.6);
}
[TestMethod]
public void Test14_5499999()
{
Assert.AreEqual((14.54999999).RoundTowards(9, 1) ,14.5);
Assert.AreEqual((14.54999999).RoundTowards(15,1), 14.6);
}
谢谢!!!
Can someone please provide code (any language will do, but I write the .Net languages and VB6) for an algorithm that rounds numbers ending with 5 towards a given number?
RoundTo(double value, double toWards, int numberOfDigitsBehindComma)
RoundTo(1.25,1,1)=1.2
RoundTo(1.25,2,1)=1.3
RoundTo(1.26,1,1)=1.3
RoundTo(1.24,2,1)=1.2
Include a solution for negative numbers please.
EDIT: There seems to be a lot of confusion about my requirements I will fill in all the Assertions that the resulting code has to meet. My solution does so.
[TestMethod]
public void RoundTowards()
{
double x=3.44;double y=3.45;double z=4.45;
double a = 3.51; double b = 4.5001; double c = -1.14; double d = -1.15;
var mean=4;
Assert.AreEqual(3.4,x.RoundTowards(mean,1));
Assert.AreEqual(3.5, y.RoundTowards(mean, 1));
Assert.AreEqual(4.4, z.RoundTowards(mean, 1));
Assert.AreEqual(3.5, a.RoundTowards(mean, 1));
Assert.AreEqual(4.5, b.RoundTowards(mean, 1));
mean = 5;
Assert.AreEqual(3.4, x.RoundTowards(mean, 1));
Assert.AreEqual(3.5, y.RoundTowards(mean, 1));
Assert.AreEqual(4.5, z.RoundTowards(mean, 1));
Assert.AreEqual(3.5, a.RoundTowards(mean, 1));
Assert.AreEqual(4.5, b.RoundTowards(mean, 1));
mean = 3;
Assert.AreEqual(3.4, x.RoundTowards(mean, 1));
Assert.AreEqual(3.4, y.RoundTowards(mean, 1));
Assert.AreEqual(4.4, z.RoundTowards(mean, 1));
Assert.AreEqual(3.5, a.RoundTowards(mean, 1));
Assert.AreEqual(4.5, b.RoundTowards(mean, 1));
Assert.AreEqual(Math.Round(-1.1,4),Math.Round( c.RoundTowards(mean, 1),4));
Assert.AreEqual(Math.Round(-1.1,4),Math.Round(d.RoundTowards(mean, 1),4));
mean = -2;
Assert.AreEqual(Math.Round(3.4,4),Math.Round( x.RoundTowards(mean, 1),4));
Assert.AreEqual(Math.Round(3.4,4),Math.Round( y.RoundTowards(mean, 1),4));
Assert.AreEqual(Math.Round(4.4,4),Math.Round( z.RoundTowards(mean, 1),4));
Assert.AreEqual(Math.Round(3.5,4),Math.Round( a.RoundTowards(mean, 1),4));
Assert.AreEqual(Math.Round(4.5,4),Math.Round( b.RoundTowards(mean, 1),4));
Assert.AreEqual(Math.Round(-1.1, 4), Math.Round(c.RoundTowards(mean, 1), 4));
Assert.AreEqual(Math.Round(-1.2, 4), Math.Round(d.RoundTowards(mean, 1), 4));
}
[TestMethod]
public void RoundTowardsTowardZero()
{
double x = 3.45; double y = -3.45;
double a = -3.551; double b = 4.551; double c = 4.5500001; double d = 4.5501;
var mean = 0;
Assert.AreEqual(3.4, x.RoundTowards(mean, 1));
Assert.AreEqual(-3.4, y.RoundTowards(mean, 1));
Assert.AreEqual(-3.6, a.RoundTowards(mean, 1));
Assert.AreEqual(4.6, b.RoundTowards(mean, 1));
Assert.AreEqual(4.5, c.RoundTowards(mean, 1));
Assert.AreEqual(4.6, d.RoundTowards(mean, 1));
}
[TestMethod]
public void Test14_55()
{
Assert.AreEqual((14.55).RoundTowards(9, 1) ,14.5);
Assert.AreEqual((14.55).RoundTowards(15,1), 14.6);
}
[TestMethod]
public void Test14_5499999()
{
Assert.AreEqual((14.54999999).RoundTowards(9, 1) ,14.5);
Assert.AreEqual((14.54999999).RoundTowards(15,1), 14.6);
}
Thanks!!!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我认为这里的所有解决方案都太复杂了。您的问题似乎是当您正好位于中点时,您希望能够控制舍入的方向。只需乘以然后除以 10 的整数次幂,就可以减少小数点后 N 位数字的问题,因此对于“5”紧随小数点后面的情况,足以解决此问题。如果你想对数字 x 进行舍入,例如 0.5 向上舍入为 1,你只需执行
如果你想对 x 进行舍入,以便 0.5 向下舍入为 0,你只需执行
这些工作,因为如果 x = 0.5,floor(x + 0.5) = Floor(1) = 1,并且 ceil(x - 0.5) = ceil(0) = 0。要看到其他数字始终正确舍入,
因此整个代码变为:
该解决方案还将整个过程卸载到实际的数学库和 CPU 的 ALU,因此非常稳健。这可以处理明显的负数,无需任何额外的调整,并且可以正确处理无穷大等。
I think all the solutions here are too complicated. Your problem seems to be that you want to be able to control the direction of rounding when you are exactly at the midpoint. You can reduce the problem of having N digits trailing the decimal point just by multiplying and then dividing by an integer power of 10, so it's enough to fix this for the case where the "5" is right after to the decimal point. If you want to round number x so that e.g. 0.5 is rounded upwards to 1, you just do
If you want to round x so that 0.5 is rounded downwards to 0, you just do
These work because if x = 0.5, floor(x + 0.5) = floor(1) = 1, and ceil(x - 0.5) = ceil(0) = 0. To see that other numbers are rounded always correctly,
so the whole code becomes:
This solution also offloads the whole process to the actual mathematics library and your CPU's ALU, and is therefore very robust. This handles obviously negative numbers without any extra tweaking, and works correctly with infinities etc.
用直接 C 编码
我不确定我完全理解这个问题,所以我做了一些假设
假设您要四舍五入 1.235,我假设您只希望 5 重要,如果您四舍五入到 5 之前的小数位。所以
RoundTo(1.235,2,1) = 1.2 但 RoundTo(1.235,2,2)= 1.24 RoundTo(1.235,1,2) = 1.23
适用于负数,它不是计算强度最小的解决方案,但应该很容易理解并修改。
Coded in straight C
I'm not sure I completely understood the question, so I made some assumptions
Say you are rounding 1.235, I assumed you only wanted the 5 to matter if you where rounding to the decimal place before the 5. So
So RoundTo(1.235,2,1) = 1.2 but RoundTo(1.235,2,2)= 1.24 and RoundTo(1.235,1,2) = 1.23
Works for negative numbers, It is not the least computationally intensive solution, but should be easy to understand and modify.
下面更新@8bitwide 的解决方案。
编辑:为了处理浮点表示中的错误,我将
value2 == 0.5
替换为isHalf(value2)
函数,该函数可以进行模糊比较。听起来这对于您的目的来说是可以的,因为您的数字来自最多数千个低精度值的计算(基于我参加过的桥牌锦标赛)。也就是说,如果出现数字 4.5500000000001,则它肯定是 4.55 的表示,而不是实际数字 4.5500000000001。
测试用例包括4.5500001。
double
的精度约为 15 位,因此,如果您得到的数字仅精确到 7 位,则表明您的计算有很大问题。Updating @8bitwide's solution below.
EDIT: to deal with error in floating point representations, I replaced
value2 == 0.5
withisHalf(value2)
function that can do a fuzzy compare. Sounds like that is OK for your purposes since your numbers come from computations on at most thousands of low-precision values (based on bridge tournaments I've attended).That is, if the number 4.5500000000001 occurs, it is surely a representation of 4.55 instead of the actual number 4.5500000000001.
The test case includes 4.5500001.
double
has about 15 digits of accuracy, so if you're getting numbers accurate to only 7 digits, something is very wrong with your calculations.好吧,这似乎可以完成工作,至少我的测试是“绿色”的。我不太喜欢最后再次舍入的必要性,因为重新添加舍入的值可能会再次产生典型的双精度数,例如 .99999999999999999999,而不是 .1
Well, this seems to do the job, at least my tests are 'green', that is. I do not particularly like the necessity to round once more in the end as, the re-addition of the value to round can again give birth to typical doubles like .99999999999999999999, in stead of .1
关于我们的讨论:
我认为你的软件对时间要求不高:也就是说,你不会每秒执行数百万次计算,而且如果计算速度太慢,软件也不会变得不可用。
为了避免浮点数或双精度数的二进制性质带来的舍入问题,我建议对所有相关计算使用十进制系统。当然,计算会比使用二进制系统慢几倍,但它应该使您的计算准确。
在 Visual Basic 6 中,有一种名为
Currency
的类型,但它是一个定点变量:它在点后始终保存 4 位数字(十进制)。 VB.NET 引入了十进制它不是固定的,但也适用于十进制系统。
我不知道它到底支持哪些数学运算,但我很确定所有基本运算都在那里。使用更复杂的函数(对数、指数、三角函数)可能需要一些破坏性的转换,但我希望您在桥接中不需要它:)
一旦进入十进制世界,您应该能够轻松实现舍入函数(例如xan 提供的),没有任何舍入问题。
我可以建议一个替代方案——到处使用整数。如果您始终只关心 - 例如 - 点后的 4 位数字,只需将所有值乘以 10000 并使用这些“增强”值执行计算即可。进行乘法运算时请注意。
In relation to our discussion:
I presume your software is not time-critical: that is --- you don't perform the computation million of times per second, and it is not the case that if it goes a bit too slow, the software will be unusuable.
To avoid the rounding problems coming from the binary nature of floats or doubles, I would suggest using decimal system for all your relevant calcuations. Of course, the computation will be few times slower than when using binary system, but it should get your computation exact.
In Visual Basic 6 there is a type called
Currency
, but it is a fixed-point variable: it holds always 4 digits (in decimal) after the dot. VB.NET introduces the Decimalwhich is not fixed, but works in decimal system as well.
I don't know exactly which mathematical operations it supports, but I am pretty sure that all basic ones are there. Using more complex ones (logarithms, exponents, trigonometric functions) may require some damaging casts, but I hope you don't need that in bridge :)
Once in the world of decimal, you should be able to easily implement the rounding function (e.g. the one provided by xan), without any rounding problems.
An alternative I can suggest -- use integers everywhere. If you always care only about - say - 4 digits after the dot, just multiply all values by 10000 and perform your computation with those "augmented" values. Just pay attention when you perform multiplication.
这个婴儿效率有点低,也许它在极端情况下会表现不佳,但它似乎对四个测试点做了正确的事情。
This baby is bit inefficient, maybe it will misbehave on the corner cases, but it appears to do the right thing for the four test points.