Печать без знака длинный длинный с использованием% d - PullRequest
2 голосов
/ 16 февраля 2010

Почему я получаю -1, когда печатаю следующее?

unsigned long long int largestIntegerInC = 18446744073709551615LL;

printf ("largestIntegerInC = %d\n", largestIntegerInC);

Я знаю, что должен использовать llu вместо d, но почему я получаю -1 вместо 18446744073709551615LL?

Это из-за переполнения?

Ответы [ 6 ]

4 голосов
/ 16 февраля 2010

В C (99), LLONG_MAX максимальное значение типа long long int гарантированно будет не менее 9223372036854775807. Максимальное значение unsigned long long int гарантированно должно быть не менее 18446744073709551615, что составляет 2 64 & minus; 1 (0xffffffffffffffff).

Итак, инициализация должна быть:

unsigned long long int largestIntegerInC = 18446744073709551615ULL;

(Обратите внимание на ULL.) Поскольку largestIntegerInC имеет тип unsigned long long int, вы должны напечатать его с правильным спецификатором формата, который является "%llu":

$ cat test.c
#include <stdio.h>

int main(void)
{
    unsigned long long int largestIntegerInC = 18446744073709551615ULL;
    /* good */
    printf("%llu\n", largestIntegerInC);
    /* bad */
    printf("%d\n", largestIntegerInC);
    return 0;
}
$ gcc  -std=c99 -pedantic test.c
test.c: In function ‘main’:
test.c:9: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘long long unsigned int’

Второй printf() выше неправильный, он может напечатать что угодно. Вы используете "%d", что означает, что printf() ожидает int, но получает unsigned long long int, который (скорее всего) не соответствует размеру int. Причина, по которой вы получаете -1 в качестве выходных данных, связана с (неудачей) и тем фактом, что на вашем компьютере числа представлены с использованием представления дополнения до двух.


Чтобы увидеть, как это может быть плохо, запустим следующую программу:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

int main(int argc, char *argv[])
{
    const char *fmt;
    unsigned long long int x = ULLONG_MAX;
    unsigned long long int y = 42;
    int i = -1;
    if (argc != 2) {
        fprintf(stderr, "Need format string\n");
        return EXIT_FAILURE;
    }
    fmt = argv[1];
    printf(fmt, x, y, i);
    putchar('\n');
    return 0;
}

На моем MacBook запуск программы с "%d %d %d" дает мне -1 -1 42, а на машине с Linux та же программа с тем же форматом дает мне -1 42 -1. К сожалению.


На самом деле, если вы пытаетесь сохранить наибольшее число unsigned long long int в вашей переменной largestIntegerInC, вы должны включить limits.h и использовать ULLONG_MAX. Или вы должны хранить задание -1 в вашей переменной:

#include <limits.h>
#include <stdio.h>

int main(void)
{
    unsigned long long int largestIntegerInC = ULLONG_MAX;
    unsigned long long int next = -1;
    if (next == largestIntegerInC) puts("OK");
    return 0;
}

В приведенной выше программе и largestIntegerInC, и next содержат максимально возможное значение для типа unsigned long long int.

3 голосов
/ 16 февраля 2010

Это потому, что вы передаете число со всеми битами, установленными в 1. Когда интерпретируется как число со знаком, дополненное двумя, то получается -1. В этом случае, вероятно, он смотрит только на 32 из этих одного бита вместо всех 64, но это не имеет никакого значения.

1 голос
/ 16 февраля 2010

В арифметике с двумя дополнениями значение со знаком -1 равно наибольшему значению без знака.

Рассмотрим битовые комбинации для отрицательных чисел в дополнении к двум (я использую 8-битные целые числа, но шаблон применяется независимо от размера):

 0 - 0x00
-1 - 0xFF
-2 - 0xFE
-3 - 0xFD

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

0 голосов
/ 16 февраля 2010

Нет, переполнения нет. Это потому, что он не печатает все значение:

18446744073709551615 совпадает с 0xFFFFFFFFFFFFFFFF. Когда printf %d обрабатывает это, он захватывает только 32 бита (или 64 бита, если это 64-битный процессор) для преобразования, и это значение со знаком -1.

Если вместо printf преобразование было %u, было бы показано либо 4294967295 (32 бита), либо 18446744073709551615 (64 бита).

Переполнение - это когда значение увеличивается до точки, где оно не помещается в выделенном хранилище. В этом случае значение выделяется просто отлично, но не полностью извлекается .

0 голосов
/ 16 февраля 2010

Вы можете (должны) понять, почему в предупреждении компилятора. Если нет, попробуйте установить самый высокий уровень предупреждения. С VS у меня есть это предупреждение: предупреждение C4245: «инициализация»: преобразование из «__int64» в «без знака __int64», несоответствие со знаком / без знака.

0 голосов
/ 16 февраля 2010

Вы использовали формат для подписанного 32-разрядного числа, поэтому вы получили -1. printf() внутренне не может сказать, насколько велико число, которое вы передали, поэтому он просто извлекает первые 32 бита из списка varargs и использует их в качестве значения, которое нужно распечатать. Поскольку вы задали подписанный формат, он печатает его таким образом, а 0xffffffff представляет собой дополнение к двоичному представлению -1.

...