我需要在Java中进行一些浮点运算,如下面的代码所示:
public class TestMain {
private static Map<Integer, Double> ccc = new HashMap<Integer, Double>() {
{ put(1, 0.01); put(2, 0.02); put(3, 0.05); put(4, 0.1); put(6, 0.2);
put(10, 0.5); put(20, 1.0); put(30, 2.0); put(50, 5.0); put(100, 10.0);
}
};
Double increment(Double i, boolean up) {
Double inc = null;
while (inc == null) {
inc = ccc.get(i.intValue());
if (up)
--i;
else
++i;
}
return inc;
}
public static void main(String[] args) {
TestMain tt = new TestMain();
for (double i = 1; i < 1000; i += tt.increment(i, true)) {
System.out.print(i + ",");
}
}
}
这是为了模拟由Betfair spinner widget作为输出给出的值范围。
Java中的浮点运算似乎会引入一些意外错误。例如,我得到2.180000000000001而不是2.18。浮点数的用途是什么,你不能相信对它们执行算术的结果?我怎样才能解决这个问题?
答案 0 :(得分:33)
如果您需要精确的十进制值,则应使用java.math.BigDecimal
。然后阅读"What Every Computer Scientist Should Know About Floating-Point Arithmetic",了解获得这些结果的背景。
(我有一个.NET-centric article,您可能会发现它更容易阅读 - 当然也更短。为了理解这个问题,Java和.NET之间的差异大多不相关。)
答案 1 :(得分:11)
浮点数使用二进制分数而不是小数分数。也就是说,你习惯于由十分位数,百分位数,千分位数等组成的小数分数.d1 / 10 + d2 / 100 + d3 / 1000 ......但是浮点数是二进制的,所以他们有半位数,四分之一位数,第八位数等.d1 / 2 + d2 / 4 + d3 / 8 ...
许多小数部分不能精确地表示为任何有限数量的二进制数字。例如,1/2是没有问题的:在十进制中它是.5,在二进制中它是.1。 3/4是十进制.75,二进制.11。但是1/10是一个干净的.1(十进制),但是二进制它是.0001100110011 ......“0011”永远重复。由于计算机只能存储有限数量的数字,因此必须将其切断,因此答案并不准确。当我们在输出时转换回十进制时,我们得到一个奇怪的数字。
正如Jon Skeet所说,如果您需要精确的小数部分,请使用BigDecimal。如果性能是一个问题,您可以滚动自己的小数部分。就像,如果你知道你总是想要3个小数位并且数字不会超过一百万左右,你可以简单地使用带有假设3个小数位的int,在你算术和写输出时根据需要进行调整format函数在正确的位置插入小数点。但99%的时间表现并不是一个值得麻烦的大问题。
答案 2 :(得分:3)
浮点数是不精确的,特别是因为它们以二进制分数(1 / 2,1 / 4,1 / 8,1 / 16,1 / 32,...)而不是小数分数(1/10)工作,1 / 100,1 / 1000,......)。只需定义您认为“足够接近”的内容并使用类似Math.abs(a-b) < 0.000001
的内容。
答案 3 :(得分:2)
在哲学上,我想知道:今天大多数计算机CPU都内置支持整数运算和浮点运算,但不支持十进制运算。为什么不?由于这种四舍五入的问题,我没有在浮动数可用的年份编写申请。您当然不能将它们用于金额:没有人想在“$ 42.3200003”的销售收据上打印价格。没有会计师会接受“我们可能会在这里和那里一分钱,因为我们使用二进制分数并且有舍入错误”。
浮子可用于测量,例如距离或温度,其中没有“精确答案”这样的东西,你必须在某些时候完成你的乐器的精确度。我想对于在化学实验室中编程计算机的人来说,常规使用浮子。但对于我们这些在商业领域的人来说,他们几乎没用。
回到我在大型机上编程的古代,IBM 360系列CPU内置了对压缩十进制算术的支持。它们存储了每个字节保存两个十进制数字的字符串,即前四位的值为0到9,而后两位为4,CPU具有操作它们的算术函数。为什么英特尔不能做那样的事情?然后Java可以添加“十进制”数据类型,我们不需要所有额外的垃圾。
当然,我不是要废除花车。只需添加小数。哦,好吧,随着伟大的社交活动的开展,我不认为这会在街头引起很多人们的兴奋或骚乱。
答案 4 :(得分:1)
您可以使用格式化输出使程序的输出看起来更像您期望的那样。
http://java.sun.com/javase/6/docs/api/java/util/Formatter.html
显然,底层浮点运算仍然有效,但至少输出会更具可读性。
例如,要将结果舍入到两位小数:
System.out.print(String.format(".2f", i) + ",");
答案 5 :(得分:0)
您可以编写一些代码来计算机器上的Epsilon。我相信std :: C ++定义它,它根据你正在使用的东西以其他方式定义。
private static float calcEpsilonFloat() {
float epsi = 1.0f;
while ((float) (1.0 + (epsi / 2.0)) != 1.0)
{
epsi /= 2.0f;
}
return epsi;
}
我唯一一次担心Epsilon是在比较信号进行阈值处理的时候。即便如此,我也不确定我是否真的需要担心它,根据我的经验,如果你担心Epsilon 你可能首先要考虑其他问题。
答案 6 :(得分:-1)
顺便说一句,你可以尝试使用这个函数来确保你的数字被重新格式化(不会有太多十进制数字),只保留你需要的小数。
n
是否包含大量小数(例如Math.PI
),
numberOfDecimals
是您需要的最大小数位数(例如,3.14为2或3.151为3)。
理论上,将负值置于numberOfDecmals
,它也会切断数字的低整数位。例如,放置n=1588.22
和numberOfDecimals=-2
,函数将返回1500.0
。
让我知道这是不是错了。