2011-12-07 17 views
13

Jestem programistą Objective-C z niewielkim doświadczeniem w C/C++ (i zerowym szkoleniem), a dziś napotkałem coś dziwnego z mocno zakodowanymi wartościami liczbowymi.Int to Double Casting Problem

Jestem pewien, że to jest proste/głupie pytanie, ale może ktoś proszę wyjaśnić, dlaczego to działa:

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 

I to też działa (numer nuta sekund uległa zmianie):

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 

Ale to jest realizowane natychmiast:

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 

jednak zamiast korzystania 4.0 od 4 poprawek go:

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 

Dlaczego 1 i 2 prawidłowo oddanych do odpowiedniej wartości podwójnym, ale większe liczby (testowałem 3 i 4) wydają się być reprezentowane 0?

Jestem kompilacji z Xcode 4.2, skonfigurowany do używania LLVM 3.0.

EDIT:

dispatch_time_t jest zdefiniowany jako:

typedef uint64_t dispatch_time_t; 

I dispatch_time jest:

dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta); 

I NSEC_PER_SEC jest:

#define NSEC_PER_SEC 1000000000 /* nanoseconds per second */ 
+0

Co to jest parametr dispatch_time? – Pubby

+0

Dzięki @Pubby, zaktualizowałem pytanie. –

+0

wydaje się bardzo dziwne, że 1,2 pracy i 3,4 nie ... Sprawdziłbym to dokładnie, ale na pewno widzisz przypadki, w których int oceni na 0f. –

Odpowiedz

25

W ciągu jednej sekundy jest 1 000 000 000 nanosekund, więc zakładam, że NSEC_PER_SEC jest zdefiniowany jako 1000000000.

  • 4 jest typu int
  • 4.0 jest typu double

Teraz zakładając, że int zawiera 32 bitów, zakres w int byłoby [-2,147,483,648 to 2,147,483,647]

4000000000 > 2147483647, dlatego Spowoduje to przepełnienie int, co spowoduje, że wartość będzie t do 0.

EDIT: Prawdopodobnie mogłem lepiej sformułować powyższe stwierdzenie. Przepełnienie może spowodować, że int (przy założeniu, że ma 32 bity, jak podano powyżej), będzie równa wartości -294967296, a dispatch_time będzie traktować dowolną wartość <= 0 jako 0 sekund. Stąd pochodzi "0".

Zmienna double może pomieścić większe wartości niż int i może przechowywać przybliżenie wartości 4000000000.

+0

Dzięki za idealnie przejrzystą odpowiedź. :) –

+1

Dokładnie na miejscu. +1. –

+1

Gdy obliczenia się przekroczą, pojawi się owinąć, a nie zero. Wartość tutaj będzie prawdopodobnie <0, a wysyłka prawdopodobnie traktuje to jako "zrób to natychmiast". –

7

Pierwsze dwie prace, ponieważ 1 * 10^9 i 2 * 10^9 mieszczą się w podpisanej int 32-bitowej. Jednak 4 * 10^9 nie pasuje do podpisanej 32-bitowej int.

4.0 * 10^9 działa, ponieważ zmiennoprzecinkowy może reprezentować tę wartość.

Spodziewam się, że to będzie działać zbyt:

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

ja nic o Objective C wiem, ale wydaje mi się, że 4 * NSEC_PER_SEC jest zbyt duża dla 32-bitowej liczby całkowitej. Używając 4.0, wymusisz mnożenie w arytmetyce zmiennoprzecinkowej i obejmiesz problem.

Aktualizacja

Może to być kod 64-bitowy, ale w niektórych językach (i wiem, że C# jest jeden) numeryczną dosłownych domyślnych do 32-bitowej liczby całkowitej, chyba że wyraźnie zdefiniować ją inaczej. To może być to, co się tutaj dzieje.

+0

Dzięki, "NSEC" to nanosekundy, więc jest to duża liczba. Zaktualizowałem moje pytanie z jego definicją. Przy okazji, jest to cały 64-bitowy kod, choć nie wiem, czy to ma znaczenie. –

+0

Zobacz moją zaktualizowaną odpowiedź. –

+0

Tak, w 32-bitowym UNIX lub 64-bitowym systemie UNIX, int jest 32-bitowy –

Powiązane problemy