Int к Двойной проблеме кастинга - PullRequest
13 голосов
/ 08 декабря 2011

Я разработчик Objective-C с небольшим опытом работы с C / C ++ (и нулевым обучением), и сегодня я столкнулся с чем-то странным с жестко закодированными числовыми значениями.

Я уверен, что это просто / глуповопрос, но может кто-нибудь объяснить, почему это работает:

NSDate *start = [NSDate date];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);

dispatch_after(popTime, dispatch_get_main_queue(), ^{
  NSLog(@"seconds: %f", [start timeIntervalSinceNow]);
});
// output: seconds: -1.0001

И это также работает (примечание количество секунд изменилось):

NSDate *start = [NSDate date];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);

dispatch_after(popTime, dispatch_get_main_queue(), ^{
  NSLog(@"seconds: %f", [start timeIntervalSinceNow]);
});
// output: seconds: -2.0001

Но это выполняется немедленно:

NSDate *start = [NSDate date];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 4 * NSEC_PER_SEC);

dispatch_after(popTime, dispatch_get_main_queue(), ^{
  NSLog(@"seconds: %f", [start timeIntervalSinceNow]);
});
// output: seconds: -0.0001

Тем не менее, использование 4.0 вместо 4 исправляет это:

NSDate *start = [NSDate date];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 4.0 * NSEC_PER_SEC);

dispatch_after(popTime, dispatch_get_main_queue(), ^{
  NSLog(@"seconds: %f", [start timeIntervalSinceNow]);
});
// output: seconds: -4.0001

Почему 1 и 2 правильно приводятся к соответствующему двойному значению, но большим числам (я проверял 3и 4) представляется как 0?

Я скомпилировал с Xcode 4.2, настроенным для использования LLVM 3.0.

РЕДАКТИРОВАТЬ:

dispatch_time_t определяется как:

typedef uint64_t dispatch_time_t;

И disp__time:

dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta);

И NSEC_PER_SEC:

#define NSEC_PER_SEC    1000000000  /* nanoseconds per second */

Ответы [ 3 ]

25 голосов
/ 08 декабря 2011

В секунду происходит 1 000 000 000 наносекунд, поэтому я предполагаю, что NSEC_PER_SEC определяется как 1000000000.

  • 4 имеет тип int
  • 4.0 имеет тип double

Теперь, предполагая, что int содержит 32 бита, диапазон int будет [-2,147,483,648 to 2,147,483,647]

4000000000 > 2147483647, поэтому вы вызовете переполнение int, в результате чего значение будет установлено равным 0.

РЕДАКТИРОВАТЬ: Возможно, я мог бы сформулировать вышеприведенный операторлучше.Переполнение может привести к тому, что int (при условии, что его размер составляет 32 бита, как указано выше) будет равно значению -294967296, а dispatch_time будет рассматривать любое значение <= 0 как 0 секунд.Отсюда и взятое выше «0».

Переменная double может содержать большие значения, чем int, и может хранить приблизительное значение 4000000000.

7 голосов
/ 08 декабря 2011

Первые два работают, потому что 1 * 10 ^ 9 и 2 * 10 ^ 9 вписываются в 32-битный int со знаком.Однако 4 * 10 ^ 9 не уместится в 32-битное целое со знаком.

4.0 * 10 ^ 9 работает, потому что с плавающей точкой может представлять это значение.

Я ожидаю, что это тоже будет работать:

dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, ((int64_t)4) * NSEC_PER_SEC);
2 голосов
/ 08 декабря 2011

Я ничего не знаю об Objective C, но я предполагаю, что 4 * NSEC_PER_SEC слишком велико для 32-разрядного целого числа.Используя 4.0, вы преобразуете умножение в арифметику с плавающей точкой и решаете проблему.

Обновление

Это может быть 64-битный код, но в некоторых языкахи я знаю, что C # - это одно) числовой литерал по умолчанию равен 32-разрядному целому числу со знаком, если вы явно не определите его иначе.Это может быть то, что здесь происходит.

...