当前位置: 首页 > 面试题库 >

不可预测的双倍

严兴言
2023-03-14
问题内容

我知道双重价值的内部代表0.2是这样的0.199999。但是下面的代码仍然让我感到困惑。

码:

public static void main(String[] args) {
    double d= 0.3d;
    double f= 0.1d;
    System.out.println(d+f);
    System.out.println(d*f);
    System.out.println(d);
    System.out.println(f);
    System.out.println(d-f);
    System.out.println(d/f);
    System.out.println((d-f)*(d-f));
}

输出:

0.4
0.03
0.3
0.1
0.19999999999999998
2.9999999999999996
0.039999999999999994

到底是怎么回事?加法,乘法进行得很好,但减法,除法则不好。谁能解释一下 加法为何与减法不同


问题答案:

简短的答案是,对于浮点运算,您存在表示错误和舍入错误。在toString()“知道”表示错误,所以如果没有舍入误差,你看不到它。但是,如果舍入误差太大,则可以。

解决方案是使用BigDecimal或舍入结果。

如果使用BigDecimal,它将显示您真正拥有的确切值。

double d = 0.3d;
double f = 0.1d;
System.out.println("d= " + new BigDecimal(d));
System.out.println("f= " + new BigDecimal(f));
System.out.println("d+f= " + new BigDecimal(d + f));
System.out.println("0.4= " + new BigDecimal(0.4));
System.out.println("d*f= " + new BigDecimal(d * f));
System.out.println("0.03= " + new BigDecimal(0.03));
System.out.println("d-f= " + new BigDecimal(d - f));
System.out.println("0.2= " + new BigDecimal(0.2));
System.out.println("d/f= " + new BigDecimal(d / f));
System.out.println("(d-f)*(d-f)= " + new BigDecimal((d - f) * (d - f)));

版画

d= 0.299999999999999988897769753748434595763683319091796875
f= 0.1000000000000000055511151231257827021181583404541015625
d+f= 0.40000000000000002220446049250313080847263336181640625
0.4= 0.40000000000000002220446049250313080847263336181640625
d*f= 0.0299999999999999988897769753748434595763683319091796875
0.03= 0.0299999999999999988897769753748434595763683319091796875
d-f= 0.1999999999999999833466546306226518936455249786376953125
0.2= 0.200000000000000011102230246251565404236316680908203125
d/f= 2.999999999999999555910790149937383830547332763671875
(d-f)*(d-f)= 0.03999999999999999389377336456163902767002582550048828125

您会注意到它0.1太大了,0.3也太小了。这意味着当您将它们相加或相乘时,您会得到一个大约正确的数字。但是,如果使用减法或除法,则会累积错误,并且得到的数字与所表示的数字相距太远。

即您可以看到0.1和0.3的结果与0.4相同,而0.3-0.1的结果与0.2不相同

顺便说一句,不使用BigDecimal就可以舍入答案

System.out.printf("d-f= %.2f%n", d - f);
System.out.printf("d/f= %.2f%n", d / f);
System.out.printf("(d-f)*(d-f)= %.2f%n", (d - f) * (d - f));

版画

d-f= 0.20
d/f= 3.00
(d-f)*(d-f)= 0.04

要么

System.out.println("d-f= " +  roundTo6Places(d - f));
System.out.println("d/f= " +  roundTo6Places(d / f));
System.out.println("(d-f)*(d-f)= " +  roundTo6Places((d - f) * (d - f)));

public static double roundTo6Places(double d) {
    return (long)(d * 1e6 + (d > 0 ? 0.5 : -0.5)) / 1e6;
}

版画

System.out.println("d-f= " +  roundTo6Places(d - f));
System.out.println("d/f= " +  roundTo6Places(d / f));
System.out.println("(d-f)*(d-f)= " +  roundTo6Places((d - f) * (d - f)));

舍入消除了舍入错误(仅保留了toString设计要处理的表示错误)

0.1之前和之后可以表示的值可以计算为

double before_f = Double.longBitsToDouble(Double.doubleToLongBits(f) - 1);
System.out.println("The value before 0.1 is " + new BigDecimal(before_f) + " error= " + BigDecimal.valueOf(0.1).subtract(new BigDecimal(before_f)));
System.out.println("The value after 0.1 is  " + new BigDecimal(f) + " error= " + new BigDecimal(f).subtract(BigDecimal.valueOf(0.1)));

版画

The value before 0.1 is 0.09999999999999999167332731531132594682276248931884765625 
    error= 8.32667268468867405317723751068115234375E-18
The value after 0.1 is  0.1000000000000000055511151231257827021181583404541015625 
    error= 5.5511151231257827021181583404541015625E-18


 类似资料:
  • 问题内容: 根据Microsoft在上的文档,NEWSEQUENTIALID的输出是可预测的。但是可预测性如何呢?假设我有一个GUID,该GUID是由生成的,要执行以下操作会有多困难: 计算下一个值? 计算先前的值? 计算第一个值? 即使根本不知道任何GUID,也要计算第一个值? 计算行数?例如,当使用整数时,告诉我应用程序中有842个订单。 以下是有关我在做什么以及各种折衷方法的一些背景信息。

  • 我怀疑这是一个定时/延迟问题,但不清楚是否有应用程序操作会强制刷新。“Drive.DriveAPI.RequestSync(GAC)”似乎没有影响。

  • 我正在尝试对我的软件的GUI进行一些自动测试,并验证一些需要比较导出文件的东西,因为我在命令行中使用了fc。 我需要我的程序和cmd的句柄才能在它们之间切换。不幸的是,Sikuli在调用时表现得非常不可预测 有时它会打开一个新的控制台,有时它会专注于已经从sikuli IDE打开的控制台。 有没有更智能、更健壮的方法来使用控制台和sikuli?

  • 问题 你需要生成在一定范围内的随机数,但你也需要对发生器进行“生成种子”操作来提供可预测的值。 解决方案 编写你自己的随机数生成器。当然有很多方法可以做到这一点,这里给出一个简单的示例。 该发生器绝对不可以以加密为目的! class Rand # 如果没有种子创建,使用当前时间作为种子 constructor: (@seed) -> # Knuth and Lewis' impro

  • 我试图建立一个长达10年的时间序列的ARIMA模型,并用它来预测未来一年。为了测试我的模型,我在9年的数据上进行训练,预测1年的数据,并将预测值与当年的实际值进行比较。 问题:我告诉预测()将其预测的期限限制为365年,即1年。但是当我绘制输出时,它似乎输出9年或大约3285的“h=”。为什么会发生这种情况? 更新:罗布·海曼下面的答案是正确的。预测周期的数量将被设置为xreg的行数,因为当使用x

  • 问题内容: 是什么使线程的执行顺序不可预测?调度程序是在某个时候使用随机数还是检查系统资源,还是查看哪个线程已经等待了足够长的时间或…? 问题答案: 调度程序通常是OS的调度程序。它受许多因素的影响,包括计算机上的其他进程正在执行的操作,硬件在执行的操作(中断)等。根据操作系统的不同,我想有时可能涉及随机数,但我通常怀疑不是。多个可变时间间隔可能会重叠,这只是一种不可预测的方式。