Подтвердить что ты не робот

Имеет ли float отрицательный ноль? (-0f)

Номера с плавающей точкой IEEE имеют бит, назначенный для обозначения знака, что означает, что вы можете технически иметь разные двоичные представления нуля (+0 и -0). Есть ли арифметическая операция, которую я мог бы сделать, например, в C, которая приводит к отрицательному значению с плавающей точкой?

Этот вопрос вдохновлен другим, который ставит под сомнение, можете ли вы безопасно сравнить 0.0f с помощью ==, и я еще раз подумал, есть ли другие способы представления нуля, которые могут привести к тому, что float1 == 0.0f сломается, казалось бы, идеально равные значения.

[Изменить] Пожалуйста, не комментируйте безопасность сравнения поплавков за равенство! Я не пытаюсь добавить к этому переполненному ведру повторяющихся вопросов.

4b9b3361

Ответ 1

В соответствии со стандартом существует отрицательный ноль, но он равен положительному нулю. Почти для всех целей они ведут себя одинаково, и многие считают, что наличие негатива является деталью реализации. Однако существуют некоторые функции, которые ведут себя совершенно по-другому: деление и atan2:

#include <math.h>
#include <stdio.h>

int main() {
    double x = 0.0;
    double y = -0.0;
    printf("%.08f == %.08f: %d\n", x, y, x == y);
    printf("%.08f == %.08f: %d\n", 1 / x, 1 / y, 1 / x == 1 / y);
    printf("%.08f == %.08f: %d\n", atan2(x, y), atan2(y, y), atan2(x, y) == atan2(y, y));
}

Результат этого кода:

0.00000000 == -0.00000000: 1
1.#INF0000 == -1.#INF0000: 0
3.14159265 == -3.14159265: 0

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

Ответ 2

Существует ли арифметическая операция я мог бы сделать, например, в C, результат в отрицательной нулевой плавающей точке значение?

Конечно:

float negativeZero = -10.0e-30f * 10.0e-30f;

Математически точный результат умножения не представляется в виде значения с плавающей запятой, поэтому он округляется до ближайшего представимого значения, которое равно -0.0f.

Семантика отрицательного нуля хорошо определена стандартом IEEE-754; единственным реальным наблюдаемым способом, в котором его поведение отличается от нуля в арифметическом выражении, является то, что если вы разделите его, вы получите другой знак бесконечности. Например:

1.f /  0.f --> +infinity
1.f / -0.f --> -infinity

Сравнение и сложение и вычитание с помощью -0.f дают тот же результат, что и с +0.f (в режиме округления по умолчанию). Умножение может сохранять знак нуля, но, как отмечено, оно обычно не наблюдается.

Существуют некоторые функции математической библиотеки, поведение которых может варьироваться в зависимости от знака нуля. Например:

copysignf(1.0f, 0.0f) -->  1.0f
copysignf(1.0f,-0.0f) --> -1.0f

Это чаще встречается в сложных функциях:

csqrtf(-1.0f + 0.0f*i) --> 0.0f + 1.0f*i
csqrtf(-1.0f - 0.0f*i) --> 0.0f - 1.0f*i

В общем, однако, вам не нужно беспокоиться о отрицательном нуле.

Ответ 3

Да, ноль может быть подписан, но для стандарта требуется положительный и отрицательный ноль для тестирования как равный

Ответ 4

Есть несколько простых арифметических операций, которые приводят к отрицательному нулевому ответу (по крайней мере, в системах i386/x64/ARMv7/ARMv8, на которых я тестировал его):

  • -1 * 0
  • 0/-1

Они застали меня врасплох, когда я писал оптимизатора для упрощения арифметических выражений. Оптимизация " a = b * 0" до " a = 0" приведет к неправильному ответу (+0), если b окажется отрицательным (правильный ответ равен -0).

Ответ 5

Да, float делает имеет отрицательный ноль, но нет, вам не нужно беспокоиться об этом при сравнении значений с плавающей запятой.

Арифметика с плавающей точкой определена для правильной работы в особых случаях.

Ответ 6

Да, float имеют отрицательный нуль, как и другие типы с плавающей точкой IEEE, такие как double (для систем с плавающей точкой IEEE). Ниже приведен пример здесь в Octave о том, как их создать; те же операции работают в C. Оператор == рассматривает +0 и -0 то же самое, и поэтому отрицательные нули не нарушают такого типа сравнения.

Ответ 7

Да, вы можете иметь +0 и -0, и это разные битовые шаблоны (не должен проходить тест равенства). Вы никогда не должны использовать == с float, но не IEEE float. < или > в порядке. Есть много других вопросов и обсуждений по этой теме, поэтому я не буду вдаваться в нее здесь.

Ответ 8

этот float1 == 0.0f никогда не является действительно безопасным сравнением.

если у вас есть что-то вроде

float x = 0.0f;
for (int i = 0; i < 10; i++) x += 0.1f;
x -= 1.0f;
assert (x == 0.0f);

он не удастся, хотя он, по-видимому, должен быть 0.

Ответ 9

Вы должны проявлять осторожность при выполнении сравнений сравнений с помощью float. Помните, что вы пытаетесь представить десятичное значение в двоичной системе.

Безопасно ли проверять значения с плавающей запятой на равенство 0?

Если вы должны сравнивать значения с плавающей запятой, я бы предложил вам использовать какой-то допуск, приемлемый для вас float1 <= toleranceVal && float1 >= toleranceVal2, или умножить на коэффициент в десять и отличить как целое. if (!(int)(float1 * 10000)) { .. some stuff .. }

Ответ 10

-lm имеет функцию signbit(), доступную для указания отрицательного значения (включая -0)