iOS вычисляет сумму файлов всегда отрицательно

У меня здесь странная проблема, и я уверен, что это просто что-то маленькое.

Я получаю информацию о файлах через JSON (RestKit делает свою работу хорошо). Я записываю размер каждого файла через coredata в локальное хранилище.

После этого в одном из моих контроллеров просмотра мне нужно суммировать размеры всех файлов в базе данных. Я извлекаю все файлы, а затем иду по склону (для), чтобы суммировать размер.

Проблема в том, что результат всегда отрицательный!

Размер файла объекта coredata имеет тип Integer 32 (размер файла сообщается в байтах JSON). Я читаю fetchresult в NSArray allPublicationsToLoad, а затем пытаюсь подвести итог. Объекты в NSArray типа CDPublication имеют значение filesize типа NSNumber:

for(int n = 0; n < [allPublicationsToLoad count]; n = n + 1)
{
    CDPublication* thePub = [allPublicationsToLoad objectAtIndex:n];
    allPublicationsSize = allPublicationsSize + [[thePub filesize] integerValue];
    sum = [NSNumber numberWithFloat:([sum floatValue] + [[thePub filesize] floatValue])];

Каждый отдельный файл одного объекта CDPublications является положительным и правильным. Только сумма всех файлов после этого становится отрицательной. Сейчас существует около 240 объектов со значениями размера файла от 4000 до 234.645.434.123.

Может кто-нибудь, пожалуйста, дайте мне удар в правильном направлении!? Проблема в том, что Integer 32 или NSNumber не могут содержать такой огромный диапазон?

Спасибо

MadMaxApp }


person MadMaxAPP    schedule 28.08.2012    source источник


Ответы (3)


Объект NSNumber не может содержать такое огромное число. Из-за способа хранения отрицательных чисел результат будет отрицательным.

Отрицательные числа хранятся с использованием дополнения до двух, это делается для сложения положительных и отрицательных чисел. Полегче. Диапазон чисел, который может содержать NSNumber, разделен на две части, старшая половина (значения int, для которых бит старшего порядка равен 1) считается отрицательной, младшая половина (где бит старшего порядка равен 0) являются нормальными положительными числами. Теперь, если вы добавите достаточно большие числа, результат будет в старшей половине и, таким образом, будет интерпретирован как отрицательное число. Вот иллюстрация для ситуации с 4-битным целым числом (32 работает точно так же, но 0 и 1 нужно напечатать намного больше;))

С помощью 4 бит вы можете представить этот диапазон целых чисел со знаком:

0000 (=0)
0001 (=1)
0010 (=2)
... 
0111 (=7)

1000 (=-8)
1001 (=-7)
...
1111 (=-1)

В этом случае максимальное положительное целое число, которое вы можете представить, равно 7. Если вы добавите 5 и 4, например, вы получите:

0101 + 0100 = 1001

1001 равно -7, когда вы представляете целые числа со знаком таким образом (а не 9, как можно было бы ожидать). Это эффект, который вы наблюдаете, но в гораздо большем масштабе (32 бита).

Ваш единственный вариант получить правильные результаты в этом случае — увеличить количество битов, используемых для представления ваших целых чисел, чтобы результат не был в диапазоне отрицательных чисел битовых комбинаций. Поэтому, если 32 бита недостаточно (как в вашем случае), вы можете использовать длинный (64 бита).

[myNumber longLongValue];
person Asciiom    schedule 28.08.2012
comment
Спасибо всем! Я новое, это было что-то действительно простое. LongLong сделал свою работу. Но поскольку я не знаю, сколько записей будет получено из coreData, мне нужно искать решение для обработки больших целых чисел или около того. На данный момент это работает. - person MadMaxAPP; 28.08.2012
comment
С long вы можете хранить довольно большие числа, так что у вас, вероятно, все будет хорошо :) - person Asciiom; 28.08.2012
comment
Чтобы быть ДЕЙСТВИТЕЛЬНО ДЕЙСТВИТЕЛЬНО безопасным, вы можете преобразовать все числа в мегабайты или что-то в этом роде и суммировать их, как только ваша сумма станет больше определенного числа. Вы получите хорошее приближение, не беспокоясь :) - person Asciiom; 28.08.2012

Я думаю, что это связано с переполнением int: очень большие целые числа интерпретируются как отрицательные, когда они переполняют размер int (32 бита). Используйте longLongValue вместо integerValue:

long long allPublicationsSize = 0;
for(int n = 0; n < [allPublicationsToLoad count]; n++) {
    CDPublication* thePub = [allPublicationsToLoad objectAtIndex:n];
    allPublicationsSize += [[thePub filesize] longLongValue];
}
person Sergey Kalinichenko    schedule 28.08.2012

Это проблема целочисленного переполнения, связанная с использованием дополнения до двух. Для 32-битного целого числа можно выразить ровно 232 (4 294 967 296) возможных целых чисел. При использовании дополнения до двух старший бит используется как бит знака, что позволяет половине чисел представлять неотрицательные целые числа (когда бит знака равен 0), а другой половине — отрицательные числа (когда бит знака равен 1). ). Это дает эффективный диапазон [-231, 231-1] или [-2 147 483 648, 2 147 483 647].

Чтобы преодолеть эту проблему для вашего случая, вам следует рассмотреть возможность использования 64-битного целого числа. Это должно хорошо работать для диапазона значений, которые вы хотите использовать. В качестве альтернативы, если даже 64-битной версии недостаточно, вам следует поискать большие целочисленные библиотеки для iOS.

person andand    schedule 28.08.2012