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

Int to Double casting issue

Я разработчик Objective-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.

EDIT:

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

typedef uint64_t dispatch_time_t;

И диспетчер_time:

dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta);

И NSEC_PER_SEC:

#define NSEC_PER_SEC    1000000000  /* nanoseconds per second */
4b9b3361

Ответ 1

В секунду есть 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.

EDIT: Я, вероятно, мог бы лучше сформулировать вышеприведенное утверждение. Переполнение может привести к тому, что int (предположим, что размер 32 бит, как указано выше) равен значению -294967296, а dispatch_time будет обрабатывать любое значение <= 0 как 0 секунд. То, откуда произошло "0".

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

Ответ 2

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

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

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

dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, ((int64_t)4) * NSEC_PER_SEC);

Ответ 3

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

Обновление

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