如何在Java中将-0.0转换为0.0?

时间:2018-10-22 04:16:27

标签: java double floating-accuracy

我正在尝试编写代码,计算给定半径和x值(正和负y值)的圆的x和y坐标。代码如下:

public class CirclePoints
{
    public static void main(String [] args)
    {
        double radius = 1;
        double x = 1;
        double y1 = 0;
        double y2 = 0;

        System.out.println("Points on a Circle with a Radius of 1.0");
        System.out.printf("%8s%8s%8s%8s", "x1", "y1", "x1", "y2");
        System.out.println("\n*************************************");
        for (x = 1; x>=-1;x -=0.1)
        {
            y1 = Math.sqrt(Math.pow(radius, 2) - (Math.pow(x, 2)));
            y2 = 0 - y1;
            System.out.printf("%8.2f%8.2f%8.2f%8.2f%n", x, y1, x, y2);
        }
    }
}

但是,无论我做什么(例如语句等),当x坐标等于-1时,负y值始终显示为-0.00。如何在打印表中将那个值设为0.00而不是-0.00?谢谢。

4 个答案:

答案 0 :(得分:1)

最适合您情况的最简单方法是,您可以检查其绝对值是否小于 epsilon 之类的值,那么该数字可能为零。以我的经验, epsilon 可能是1e-9,这可能足以加倍。

从@PeterLawrey 更新: epsilon 可能是%8.2f 0.5e-2

答案 1 :(得分:0)

这不是您的代码。这是执行计算的方式。有关更多信息,请参见以下内容。

  

将无限多个实数压缩为有限数量的位   需要一个近似的表示。 ...因此a的结果   浮点计算通常必须四舍五入才能适应   变成有限的表示此舍入误差是   浮点计算的特征。资源:   https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

您始终可以使用Math.abs(value)转换为正值。

答案 2 :(得分:0)

0.1中的浮点错误(在二进制位中没有有限的近似值)将乘以for循环二十倍。

可以通过以下方式获得更好的行为:

for (int x10 = 10; x10 >= -10; --x10) {
    x = x10 / 10.0;

或者,如果允许您躺在代码中,则对printf进行的小幅递增校正将做到:

       double ks = x + 0.00001;
       System.out.printf("%8.2f%8.2f%8.2f%8.2f%n", ks, y1, ks, y2);

答案 3 :(得分:0)

上一次迭代中y的实际值约为-2.1•10 -8 (有时显示为“ -2.1e-8”或“ -.000000021”)。它显示为“ -0.00”,因为您使用的位数有限。这意味着您的问题的标题不在目标范围内:没有-0.0可以转换为0.0。您实际上有一个小的负值,而不是-0

其他答案也建议了解决方法,例如手动测试一个较小的值并将其替换为零。尽管简单的解决方法可能会在您要问的特定情况下起作用,但它们是粗略的建议(例如采用绝对值或将绝对值与0.5e-2进行比较),但存在一些缺陷。例如,在后者中,适当的测试是Math.abs(y) <= 0.5e-2还是Math.abs(y) < 0.5e-2?要知道要使用<还是<=,需要知道源文本0.5e-2是通过转换为double来向上还是向下取整。尽管可以很容易地确定一个特定值(通过打印转换结果的完整十进制值并将其直观地与0.05进行比较,但是如果结果低于0.05,则使用<=,如果结果低于<以上),对于一般的值来说并不是那么容易。一种获得所需格式的结果的正确方法是打印到缓冲区,然后检查结果是否仅包含零位,如果有,则将“-”更改为空格。

您应该了解您正在尝试在此处纠正错误的内容。在零前面有一个负号没有错。但是,这可能是程序中其他错误的提示。

Java对浮点数使用基于二进制的格式。在这种格式下,不能精确表示0.1,这意味着循环中的x值永远不会精确地是0.1的倍数,除非在第一次迭代中以1开始。

在Java中,源文本0.1转换为最接近的可表示值,即0.1000000000000000055511151231257827021181583404541015625。 (尽管我用许多十进制数字写了该值,但它恰好是一个53位整数除以2的幂。)在循环的第一次迭代中,x的值是0.90000000000000002220446049250313080847263336181640625。在最后一次迭代中,它的值为-0.9999999999999997779553950749686919152736663818359375。由于此最后一个值不完全为-1,所以y也不完全为零。

这里的一个严重问题是,当您编写这样的循环时,如果使用不同的步长,则不能保证以接近-1的值结束。每次执行浮点算术时,精确的数学结果都会四舍五入到最接近的可表示值,并且这种舍入可能导致值增加或减少。考虑一下当循环快要结束时发生的情况,并且x的加法产生−1.0000000000000002…而不是−0.9999999999999997…然后测试x >= -1报告为false,并且循环不执行最终迭代预期。例如,如果步长为0.01而不是0.1,则上一次迭代的x等于-0.99000000000000143440814781570225022733211517333984375。 x应该接近-1的最后一次迭代不会执行,因为x的实际计算值是−1.000000000000001332267629550187848508358001708984375。

在简单循环中避免该问题的经典方法是对循环控制使用无舍入误差的算术,然后将迭代器的值缩放到所需范围。就您而言,您可以使用:

for (i = 10; i >= -10; i -= 1)
{
    x = i / 10.;
    …
}

此处i可以是整数类型或浮点类型。由于所有整数都可以用Java double最多2 53 表示,因此该范围内的整数结果算术运算没有舍入误差。