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 */
Ответы
Ответ 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-разрядному значению целого числа, если вы явно не указали его иначе. Возможно, это происходит здесь.