Как получить результат -0 в вычислениях с плавающей запятой и отличить его от +0 в С#?

В документации MSDN упоминается, что тип double включает отрицательный нуль. Однако и -1.0 / double.PositiveInfinity, и -double.Epsilon / 2 возвращают обычный 0 (и равны ему). Как я могу получить -0?


person Alexey Romanov    schedule 25.06.2009    source источник
comment
Хорошо, мне любопытно: поскольку они математически одинаковы, почему вы хотите отличить +0 от -0?   -  person Beska    schedule 25.06.2009
comment
Потому что я хочу показать разницу между обычными математическими вычислениями и вычислениями с плавающей запятой.   -  person Alexey Romanov    schedule 26.06.2009


Ответы (5)


Вот практический пример различия между ними без изучения битов. Ссылки MSDN здесь и здесь помог мне создать этот пример.

static void Main(string[] args)
{
    float a = 5 / float.NegativeInfinity;
    float b = 5 / float.PositiveInfinity;
    float c = 1 / a;
    float d = 1 / b;
    Console.WriteLine(a);
    Console.WriteLine(b);
    Console.WriteLine(c);
    Console.WriteLine(d);
}

Вывод:

0
0
-Infinity
Infinity

Обратите внимание, что -0 и 0 выглядят одинаково для сравнения, вывода и т. д. Но если вы разделите на них 1, вы получите -бесконечность или бесконечность, в зависимости от того, какой у вас ноль.

person Brian    schedule 25.06.2009
comment
› Обратите внимание, что -0 и 0 выглядят одинаково для сравнения, вывода и т. д. Спасибо, это то, что я пропустил. - person Alexey Romanov; 25.06.2009

Отрицательный ноль связан со способом хранения числа в двоичном виде, а не с реальным достижимым результатом математического расчета.

В памяти с плавающей запятой самый верхний бит часто используется для обозначения знака. Это оставляет 31 бит для данных (в 32-битном значении с плавающей запятой), поэтому на самом деле есть два представления для нуля.

00000000 00000000 00000000 00000000
Or
00000000 00000000 00000000 00000001

Оба представляют ноль, но один с отрицательным битом знака.

Естественно, это обычно происходит, когда вы увеличиваете максимально возможное положительное число, оно переполняется обратно к отрицательному нулю.

Однако в .net я думаю, что по умолчанию тип выполняет проверки переполнения и выдает исключение, а не позволяет вам переполняться, поэтому единственный способ действительно заархивировать это значение — установить его напрямую. Кроме того, -0 всегда должен равняться +0.

Подробнее об этом читайте на Википедии.

person Simon P Stevens    schedule 25.06.2009
comment
На самом деле, Wiki говорит, что пока можно получить отрицательный нуль в результате выражения (например, в результате арифметического переполнения отрицательного числа) для языков, включая C#. - person Alexey Romanov; 25.06.2009
comment
арифметическое недополнение отрицательного числа. Да, это просто обратная сторона переполнения самого высокого положительного числа в моем примере. В любом случае ответ Брайана намного лучше моего, послушайте его =:) - person Simon P Stevens; 25.06.2009

Один из способов — использовать BitConverter.GetBytes. Если вы проверите байты, вы увидите, что бит знака для значения фактически установлен, указывая на то, что оно отрицательное.

byte[] zeroBytes = BitConverter.GetBytes(zero);
byte[] negZeroBytes = BitConverter.GetBytes(negZero);

bool sameBytes = zeroBytes[7] == negZeroBytes[7];
person Zanoni    schedule 25.06.2009
comment
Кажется, это не работает для меня. Во-первых, ноль и отрицательный нуль, что это такое? Во-вторых, если я делаю их двойными и устанавливаю значения 0 и -0, я просто получаю массив байтов, которые все равны 0 для обоих. - person Simon P Stevens; 25.06.2009

Попробуй это. Если pz является положительным нулем, а nz отрицательным нулем:

Double.PositiveInfinity/pz => Double.PositiveInfinity
Double.PositiveInfinity/nz => Double.NegativeInfinity

Я получил это из спецификации ECMA C#.

Вы можете получить отрицательный ноль, разделив любое положительное число на отрицательную бесконечность:

10.0/Double.NegativeInfinity
person Joel Goodwin    schedule 25.06.2009

После проверки я вижу, что -1.0 / double.PositiveInfinity действительно возвращает -0. Действительно, 1.0 / (-1.0 / double.PositiveInfinity) возвращает -infinity.

person Alexey Romanov    schedule 25.06.2009