Чи можна розрізнити 0 і -0?


94

Я знаю, що цілі значення 0і -0по суті однакові. Але мені цікаво, чи можна їх розрізнити.

Наприклад, як мені дізнатися, чи була призначена змінна -0?

bool IsNegative(int num)
{
    // How ?
}

int num = -0;
int additinon = 5;

num += (IsNegative(num)) ? -addition : addition;

Чи значення, -0збережене в пам'яті, точно так само, як 0?


9
Для цілих чисел різниці немає.
Марун

14
Це залежить від реалізації, але для реалізацій, де intпредставлено в додатку 2 (на сьогоднішній день найчастіше зустрічається), 0і -0мають однакові побітові подання.
Манкарс

11
У машині доповнення 2 немає різниці на рівні бітів.
Марко А.

17
@VirtualSnake: Що означає "у двійковому вигляді"? Є, насправді, бінарні кодування , для яких є відмінність між 0 і 0. Знак і величиною, наприклад.
Бенджамін Ліндлі,

8
@VirtualSnake Правильно, ми говоримо про це int. Див . Кодування додатків .
CiaPan

Відповіді:


112

Це залежить від машини, на яку ви націлюєтесь.

На машині, яка використовує представлення доповнення 2 для цілих чисел, немає різниці на рівні бітів між 0і -0(вони мають однакове представлення)

Якщо ваша машина використовувала чиюсь доповнення , ви точно могли б

0000 0000   -> signed01111 1111   -> signed   0

Очевидно, ми говоримо про використання власної підтримки , процесори серії x86 мають рідну підтримку для представлення доповнених двох номерів із підписами. Використання інших подань, безумовно, можливо, але, мабуть, було б менш ефективним і вимагало б більше вказівок.

(Як зауважив також Джері Коффін: навіть якщо доповнення розглядалося здебільшого з історичних причин, подані знакові величини все ще досить поширені і мають окреме представлення для негативного та позитивного нуля)


6
@TobiMcNamobi: Напевно недостатньо, щоб колись турбуватися. Я був би здивований, якщо хтось коли-небудь потрудився перенести компілятор C ++ для отримання вихідних даних для такої машини.
Бенджамін Ліндлі,

1
Я погоджуюся з Бенджаміном, історично існували машини, які його використовують, але в наш час я випадково не знаю виробничих машин, які його використовують. Проте це завжди добре знати і пам’ятати.
Марко А.

4
@TobiMcNamobi порозрядне доповнення до сих пір використовується в UNISYS 2200 Система stackoverflow.com/a/12277974/995714 stackoverflow.com/q/6971886/995714
phuclv

2
Я ніколи не дивився на вимоги своїх комплементу - робить стандарт фактично гарантією того, що 0і -0є різні ? Чесно кажучи, я би очікував, що він поводитиметься більше, як дозволити двобітові подання одного і того ж значення, і ваша програма може використовувати будь-який із них.

8
@Hurkly: ні, навіть якщо існує від’ємне нульове подання, стандарт не гарантує, що присвоєння або ініціалізація за допомогою виразу -0, тобто результат застосування унарного -оператора до цілочислової константи 0, є від’ємним нульовим поданням. Незалежно від подання, стандарт ніколи не говорить 0і -0це математично різні значення, лише те, що може існувати негативно-нульовий бітовий шаблон. Якщо є, це все одно представляє те саме числове значення, 0.
Стів Джессоп,

14

Для int(у майже універсальному поданні "доповнення 2") подання 0та -0однакові. (Вони можуть бути різними для інших подань чисел, наприклад. IEEE 754 з плаваючою комою.)


9
>> Припускаючи представлення доповнення 2
Марко А.

12

Почнемо з того, що представляємо 0 у додатку 2 (звичайно, існує безліч інших систем і подань, тут я посилаюся саме на цю), припускаючи, що 8-біт, нуль:

0000 0000

Тепер давайте перегорнемо всі біти і додамо 1, щоб отримати доповнення 2:

1111 1111 (flip)
0000 0001 (add one)
---------
0000 0000

ми отримали 0000 0000 , і це також представлення -0.

Але зверніть увагу, що в додатку 1 підписаний 0 дорівнює 0000 0000, але -0 дорівнює 1111 1111.


1
Чи можу я знати, чому, проти, щоб покращити свою відповідь, будь ласка?
Марун

1
Хоча більшість інших відповідей технічно правильні, ваша відповідь практична і забезпечує реалізацію. Добре.
umlcat

9

Я вирішив залишити цю відповідь, оскільки реалізації С та С ++ зазвичай тісно пов’язані, але насправді це не відповідає стандарту С, як я думав. Справа залишається в тому, що стандарт C ++ не визначає, що відбувається у таких випадках. Також доречно, що подання, що не доповнюють два, надзвичайно рідкісні у реальному світі, і що навіть там, де вони існують, вони часто приховують різницю в багатьох випадках, а не виставляють її як щось, що хтось може легко очікувати виявити.


Поведінка від’ємних нулів у цілочисельних представленнях, у яких вони існують, не настільки суворо визначена у стандарті С ++, як у стандарті С. Однак він посилається на стандарт С (ISO / IEC 9899: 1999) як нормативне посилання на верхньому рівні [1.2].

У стандарті C [6.2.6.2] від’ємний нуль може бути результатом побітових операцій або операцій, де вже присутній від’ємний нуль (наприклад, множення або ділення від’ємного нуля на значення або додавання від’ємного нуля до нуль) - застосування одинарного мінусового оператора до значення нормального нуля, як у вашому прикладі, гарантовано приведе до нормального нуля.

Навіть у тих випадках, коли може генеруватися від’ємний нуль, немає жодної гарантії, що вони будуть, навіть у системі, яка підтримує від’ємний нуль:

Не визначено, чи справді ці випадки генерують від’ємний нуль чи нормальний нуль, і чи від’ємний нуль стає нормальним нулем при зберіганні в об’єкті.

Тому можна зробити висновок: ні, немає надійного способу розкрити цю справу. Навіть якби не той факт, що подання, що не доповнюють двійки, дуже рідкісні в сучасних комп’ютерних системах.

Стандарт C ++, зі свого боку, не згадує термін "негативний нуль" і дуже мало обговорює деталі підписаної величини та подання власних доповнень, за винятком того, щоб зазначити [3.9.1, пункт 7], що вони дозволені.


Взагалі ні, той факт, що щось є правдою / вимагається в C , не обов'язково означає, що це правда / вимагається в C ++. Той факт, що С є нормативним посиланням, означає, що С ++ посилається на стандарт С для різних речей (головним чином, вміст стандартних заголовків), але визначення цілочисельних типів не є одним із цих речей. Однак відсутність гарантованого способу отримання від’ємного нуля означає, що те, що ви робите висновок, все ще відповідає дійсності, не існує надійного способу його створення за допомогою арифметики, навіть якщо представлення існує.
Steve Jessop

Тоді чому стандарт С ++ вдається набагато менше деталей до подібних речей?
Random832

1
Я вважаю, що особистий смак, якщо кількість людей, які голосують за стандарт C ++, можна вважати "особистим" :-) Якщо ж воно збирається відповідати стандарту C для визначень, тоді воно могло б зробити це належним чином. і не містять деталей, як це відбувається в деяких інших випадках.
Steve Jessop

Чи "C ++ - це мова програмування загального призначення, заснована на мові програмування C, як описано у ISO / IEC 9899: 1999 Мови програмування - C (далі - стандарт С)". [1.1 параграф 2] має якесь нормативне значення? Я думав, що це було призначено для загального включення стандарту C для будь-чого, що не було спеціально замінено стандартом C ++.
Random832

@ Просто історична довідка Random832 Ні , це (є, наприклад, немає _Boolабо _Complexабо призначених ініціалізатор або складових літерали в C ++). Стандарт С ++ знає, як включити стандарт С, коли він цього хоче, наприклад, [basic.fundamental] / p3: "Цілі типи з підписом та без знака повинні відповідати обмеженням, наведеним у стандарті С, розділ 5.2.4.2.1."
ТК

8

Якщо ваша машина має різні подання для -0і +0, тоді memcmpви зможете їх розрізнити.

Якщо присутні біти заповнення, можливо, насправді можуть бути кілька подань і для значень, відмінних від нуля.


5

У специфікації мови C ++ немає такого int, як від’ємний нуль .

Єдине значення, яке мають ці два слова, - це унарний оператор, -до якого застосовується 0, так само, як три плюс п’ять - це лише двійковий оператор, +застосований до 3і5 .

Якби був чіткий від’ємний нуль , доповнення двох (найпоширеніше представлення цілих типів) було б недостатнім для реалізації на C ++, оскільки немає можливості представити дві форми нуля.


Навпаки, плаваючі крапки (за IEEE) мають окремі позитивні та негативні нулі. Їх можна розрізнити, наприклад, при діленні на них 1. Позитивний нуль породжує позитивну нескінченність; негативний нуль породжує негативну нескінченність.


Однак, якщо трапляються різні подання пам'яті int 0 (або будь-яке int, або будь-яке інше значення будь-якого іншого типу), ви можете використати, memcmpщоб виявити, що:

#include <string>

int main() {
    int a = ...
    int b = ...
    if (memcmp(&a, &b, sizeof(int))) {
        // a and b have different representations in memory
    }
}

Звичайно, якби це сталося, поза безпосередніми операціями пам'яті, ці два значення все одно працювали б абсолютно однаково.


3
Насправді, мова, яка не вимагає свого існування, не означає, що вона вимагає її відсутності. Підказка: Це не вимагає жодного.
Дедулікатор

2
@Deduplicator, свого роду. Під "мовою C ++" я маю на увазі "в специфікації мови C ++ ". Оскільки в специфікації також не згадується про фрообінатори, я міг би сказати, що "C ++ не має фреобінаторів" без надто великої двозначності. Я думав, що це зрозуміло, але буду вдосконалювати.
Пол Дрейпер

1
Мовна специфікація також не згадує єдинорогів.
ypercubeᵀᴹ

2

Для спрощення мені було простіше візуалізувати.

Тип int (_32) зберігається з 32 бітами . 32 біти означають 2 ^ 32 = 4294967296 унікальних значень . Отже:

діапазон даних без підпису - від 0 до 4 294 967 295

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

У випадку доповнення One існує значення -0.


2
Я не голосував за, але платформи, для яких intне зберігається 32 біти, сьогодні популярніші, ніж платформи з їхнім доповненням.
Maciej Piechotka
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.