Przesyłaj na podłogę


120

Czy jest jakaś różnica między tymi:

float foo1 = (int)(bar / 3.0);
float foo2 = floor(bar / 3.0);

Jak rozumiem, oba przypadki mają ten sam wynik. Czy jest jakaś różnica w skompilowanym kodzie?


1
trochę lepiej z floor, ale uważaj, to doublenie jest dla float. C99 ma również floorfdla float.
Jens Gustedt

2
Więc mają ten sam wynik, o ile słupek jest dodatni
Zac

1
(uwaga: w C ++ proszę #include<cmath>i używaj std::floor)
user202729

Jaki to typ bar?
chux - Przywróć Monikę

@chux Nie ma znaczenia, podzielenie przez 3,0 i tak
da

Odpowiedzi:


193

Rzutowanie na int zostanie obcięte do zera. floor()skróci się w kierunku ujemnej nieskończoności. To da ci różne wartości, jeśli barbyły ujemne.


15
Myślę, że trafiłeś tutaj w sedno. Inną różnicą, jeśli taka floor()jest intencja, jest to, czy wartość barjest zbyt duża, aby zmieścić się w int.
Fred Larson,

Czy masz jakieś źródło tego stwierdzenia?
HelloGoodbye

1
Nawet jeśli wynik jest pozytywny, nie jest to gwarantowane. Zobacz to i to .
user202729

27

Jak powiedziano wcześniej, dla liczb dodatnich są one takie same, ale różnią się dla liczb ujemnych. Zasada jest taka, że ​​int zaokrągla się w kierunku 0, podczas gdy floor zaokrągla w kierunku ujemnej nieskończoności.

floor(4.5) = (int)4.5 = 4
floor(-4.5) = -5 
(int)(-4.5) = -4

To powiedziawszy, istnieje również różnica w czasie wykonania. W moim systemie mierzyłem czas, że rzucanie jest co najmniej 3 razy szybsze niż podłoga.

Mam kod, który wymaga działania z ograniczonym zakresem wartości, w tym liczb ujemnych. I musi być bardzo wydajne, więc używamy do tego następującej funkcji:

int int_floor(double x) 
{ 
    return (int)(x+100000) - 100000; 
}

Oczywiście to się nie powiedzie dla bardzo dużych wartości x (napotkasz problemy z przepełnieniem) i dla wartości ujemnych poniżej -100000 itd. Ale taktowałem tak, aby był co najmniej 3 razy szybszy niż floor, co było naprawdę krytyczne dla naszej aplikacji. Weź to z przymrużeniem oka, przetestuj na swoim systemie itp., Ale warto rozważyć IMHO.


„Mam taktowanie, aby było co najmniej 3 razy szybsze niż podłoga” -> OP używa float, nie double- być może doublebyła to Twoja aplikacja. Jeśli w C, pamiętaj, aby użyć floorf()z floats.
chux - Przywróć Monikę

@chux Myślę, że jedyną różnicą jest to, że rzutowanie pozwala na optymalizację w czasie kompilacji. Więc ta konwersja mogła zostać całkowicie usunięta podczas wykonywania.
ClydeTheGhost

9

SO 101, nie zmieniaj swojego pytania po tym, jak ludzie odpowiedzieli na Twoje pytanie, zamiast tego napisz nowe pytanie.

Jak myślisz, dlaczego będą miały ten sam wynik?

float foo = (int)(bar / 3.0) //will create an integer then assign it to a float

float foo = fabs(bar / 3.0 ) //will do the absolute value of a float division

bar = 1.0

foo1 = 0;
foo2 = 0.33333...

1
Co masz na myśli fabs? Pytanie dotyczyło floor. Podłoga 0.33333... jest 0.
Aaron Franke,

2
@AaronFranke oryginalne pytanie zostało zmodyfikowane. wydaje się, że wiele może się wydarzyć w ciągu 8 lat ;-) zauważ, że inne odpowiedzi mają to samo założenie
AndersK

4

EDYCJA: Ponieważ pytanie mogło zostać zmodyfikowane z powodu pomyłki między fabs()a floor().

Biorąc pod uwagę oryginalne przykładowe wiersze pytania:

1.  float foo = (int)(bar / 3.0);

2.  float foo = fabs(bar / 3.0);

Różnica polega na tym, że jeśli słupek jest ujemny, wynik będzie ujemny z pierwszym, ale dodatni z drugim. Pierwsza zostanie obcięta do liczby całkowitej, a druga zwróci pełną wartość dziesiętną, w tym część ułamkową.


3

Tak. fabszwraca wartość bezwzględną swojego argumentu, a rzutowanie na wartość int powoduje obcięcie dzielenia (w dół do najbliższej liczby całkowitej), więc wyniki będą prawie zawsze inne.


2

Istnieją dwie główne różnice:

  1. Jak inni zauważyli, rzutowanie na liczbę całkowitą będzie skracane do zera, podczas gdy floor()zawsze będzie obcięte w kierunku ujemnej nieskończoności; jest to inne zachowanie dla argumentu ujemnego.

  2. Wydaje się, że nikt (jeszcze) nie zwrócił uwagi na inną różnicę - jeśli argument jest większy lub równy MAX_INT+1(lub mniejszy niż -MAX_INT-1), rzutowanie na a intspowoduje odrzucenie najwyższych bitów (prawdopodobnie C) lub niezdefiniowane zachowanie ( C ++ i prawdopodobnie C). Na przykład, jeśli masz int32 bity, będziesz mieć tylko bit znaku plus 31 bitów danych. Więc użycie tego z doubledużym rozmiarem da niezamierzone rezultaty.


2.a. Dokładnym warunkiem konwersji na intprzepełnienie jest to, że argument jest większy lub równy INT_MAX+1. Symetrycznie warunkiem niedomiaru jest to, że argument jest mniejszy lub równy INT_MIN-1.
Pascal Cuoq

1
2.b. Przepełnienie konwersji ze zmiennoprzecinkowej na liczbę całkowitą jest niezdefiniowanym zachowaniem w C ++. Nie powoduje „odrzucenia największej liczby bitów”. Zobacz (chociaż jest napisane dla C): blog.frama-c.com/index.php?post/2013/10/09/…
Pascal Cuoq

0

(int) xjest żądaniem zachowania części całkowitej x(nie ma tu zaokrąglania)

fabs(x)= | x | tak, że jest >= 0;

Np .: (int) -3.5zwraca -3; fabs(-3.5)zwroty 3.5;

Ogólnie fabs (x) >= xdla wszystkich x;

x >= (int) x Jeśli x >= 0

x < (int) x Jeśli x < 0


x = -3 fabs (-3) = 3 (int) -3 = -3; Myślę, że utrzymują się ostatnie nierówności. Czy możesz wyjaśnić, dlaczego jest źle?
Paul Hoang,

Przepraszam, miałem na myśli -3,5, podany przykład. -3> -3,5
Dennis Zickefoose

3
Ostatnią instrukcją nadal powinno być „x <= int (x), jeśli x <0”, a nie „x <(int) x, jeśli x <0”: ujemne liczby całkowite pozostają takie same.
Tomasz Gandor
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.