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

How to convert -0.0 to 0.0 in Java?

提问人:maryloo 提问时间:10/22/2018 最后编辑:MWBmaryloo 更新时间:10/22/2018 访问量:1300

问:

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

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 时,无论我做什么(if 语句等),负 y 值都会一直显示为 -0.00。如何使打印表中的一个值为 0.00 而不是 -0.00?谢谢。

Java 双倍 浮动精度

评论

0赞 Alexis Villar 10/22/2018
你试过用吗?Math.abs()
1赞 Tim Biegeleisen 10/22/2018
Java 中的 float 和 double(以及大多数编程语言)并不准确。您可能需要考虑使用精确的数学(包括精确的零值)。BigDecimal
0赞 chrylis -cautiouslyoptimistic- 10/22/2018
特别要注意的是,这在二进制浮点运算中并不完全可表示。0.1
0赞 VGR 10/22/2018
作为实验,尝试添加到循环体的末尾。正如其他人所提到的,0.1 的浮点表示并不完全是 0.1,因此 y1 和 y2 很可能不完全为零,而是非常小的值,在 printf 语句中被四舍五入到小数点后两位。System.out.println(y2);

答:

1赞 Le Duong Tuan Anh 10/22/2018 #1

可能适合您情况的最简单的事情,您可以检查它的绝对值是否小于 epsilon 之类的东西,那么该数字可能为零。根据我的经验,epsilon 可能是 1e-9,这可能足以翻倍。

从 @PeterLawrey 更新:对于您的 %8.2f,epsilon 可能是 0.5e-2

评论

1赞 Peter Lawrey 10/22/2018
由于 OP 打印的是 2 位小数,因此 epsilon 可能是 0.5e-2
0赞 Peter Lawrey 10/22/2018
1e-9 对于各种用例来说是一个合理的数字,无需更多信息。
0赞 MWB 10/22/2018 #2

它不是你的代码。这是执行计算的方式。有关详细信息,请参阅以下内容。

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

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

0赞 Joop Eggen 10/22/2018 #3

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

可以预期更好的行为来自:

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);
0赞 Eric Postpischil 10/22/2018 #4

上次迭代中的实际值约为 −2.1•10−8(有时显示为“-2.1e-8”或“-.000000021”)。它显示为“-0.00”,因为您用有限的数字打印了它。这意味着您的问题标题偏离了目标:没有可转换为 .你实际上有一个很小的负值,而不是.y-0.00.0-0

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

你应该明白你试图在这里纠正错误的事情。在零前面加负号并没有错。但是,这是一个线索,表明您的程序中存在其他错误。

Java uses a binary-based format for floating-point numbers. In this format, 0.1 cannot be exactly represented, and that means the value in your loop is never exactly a multiple of 0.1, except in the first iteration, when it starts with 1.x

In Java, the source text is converted to the nearest representable value, which is 0.1000000000000000055511151231257827021181583404541015625. (Although I have written this value with many decimal digits, it is exactly a 53-bit integer divided by a power of two.) In the first iteration of your loop, has the value 0.90000000000000002220446049250313080847263336181640625. In the last iteration, it has the value −0.9999999999999997779553950749686919152736663818359375. Since this last value is not exactly −1, is not exactly zero.0.1xy

A serious problem here is that, when you write a loop like this, it is not guaranteed to end with a value near −1 if you use a different step size. Every time you perform floating-point arithmetic, the exact mathematical result is rounded to the nearest representable value, and that rounding may result in the value increasing or decreasing. Consider what happens when the loop is nearing its end, and the addition to produces −1.0000000000000002… rather than −0.9999999999999997… Then the test reports false, and the loop does not perform the final iteration you expected. For example, if the step size had been 0.01 instead of 0.1, the last iteration would have been with equal to −0.99000000000000143440814781570225022733211517333984375. The last iteration, where should be near −1, is never performed, because the actual computed value for is −1.000000000000001332267629550187848508358001708984375.xx >= -1xxx

A classic way to avoid that problem in simple loops is to use arithmetic with no rounding errors for the loop control, and then the scale the iterator value to the desired range. In your case, you could use:

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

Here can be an integer type or a floating-point type. Since all integers are representable in Java’s up to 253, there are no rounding errors in this range for arithmetic with integer results.idouble

0赞 Martijn Courteaux 12/13/2023 #5

To answer to the question posed in the title of this post: how to convert -0.0 to +0.0? is done by adding +0.0 to your result.

-0.0 + 0.0 == 0.0

Adding zero is a no-op for all other cases. Only gets affected and becomes .-0.0+0.0