Как конвертировать -0.0 в 0.0 в Java? - PullRequest
0 голосов
/ 22 октября 2018

Я пытаюсь написать код, который вычисляет координаты 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 голосов
/ 22 октября 2018

Фактическое значение 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-разрядное целое число, деленное на степень два.) На первой итерации вашего цикла x имеет значение 0,90000000000000002220446049250313080847263336181640625.На последней итерации оно имеет значение -0,999999999999997779553950749686919152736663818359375.Так как это последнее значение не точно равно -1, y не совсем равно нулю.

Серьезная проблема здесь заключается в том, что, когда вы пишете цикл, подобный этому, не гарантируется, что он закончится значением, близким к -1, если вы используете другой размер шага.Каждый раз, когда вы выполняете арифметику с плавающей запятой, точный математический результат округляется до ближайшего представимого значения, и это округление может привести к увеличению или уменьшению значения.Рассмотрим, что происходит, когда цикл приближается к концу, и добавление к x дает -1,0000000000000002… вместо −0,99999999999999997… Тогда тест x >= -1 сообщает об ошибке, и цикл не выполняет ожидаемую последнюю итерацию.Например, если размер шага был 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 , в этом диапазоне нет ошибок округления для арифметики с целочисленными результатами.

0 голосов
/ 22 октября 2018

Это не ваш код.Это способ расчета.Для получения дополнительной информации см. Следующее.

Для сжатия бесконечного числа действительных чисел в конечное число бит требуется приблизительное представление.... Следовательно, результат вычисления с плавающей точкой часто должен быть округлен, чтобы соответствовать его конечному представлению.Эта ошибка округления является характерной особенностью вычисления с плавающей точкой.source: https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Вы всегда можете использовать Math.abs (значение) для преобразования в положительное значение.

0 голосов
/ 22 октября 2018

Ошибка с плавающей запятой в 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);
0 голосов
/ 22 октября 2018

Самая простая вещь, которая может подходить для вашей ситуации, вы можете проверить, если абсолютное значение меньше чем что-то вроде эпсилон , тогда это число может быть нулем.По моему опыту, эпсилон возможно 1e-9, что может быть достаточно для двойного.

Обновлено с @ PeterLawrey : epsilon возможно 0.5e-2 для вашего % 8,2f .

...