Najlepszy sposób na uzyskanie części całkowitej liczby dziesiętnej

89

Jaki jest najlepszy sposób na zwrócenie części całkowitej liczby ułamka dziesiętnego (w języku C #)? (To musi zadziałać dla bardzo dużych liczb, które mogą nie pasować do int).

GetIntPart(343564564.4342) >> 343564564
GetIntPart(-323489.32) >> -323489
GetIntPart(324) >> 324

Celem tego jest: wstawiam do pola dziesiętnego (30,4) w bazie danych i chcę się upewnić, że nie próbuję wstawiać liczby, która jest zbyt długa dla tego pola. Częścią tej operacji jest określenie długości całkowitej części dziesiętnej.

Yaakov Ellis
źródło
Nie możesz dostać części int; można uzyskać część całkowitą, a porzucić część ułamkową. Część liczbowa Decimal może łatwo przepełnić int i wyrzucić lub zawinąć, po cichu zabijając kod.
1
Cóż, dlatego to pytanie nie jest tak proste, jak się wydaje. Potrzebuję tego, aby działał z bardzo dużymi liczbami tak samo niezawodnie, jak w przypadku małych liczb. Jednak „liczba całkowita” jest dokładniejsza niż „int” - przeformułuję powyżej.
Yaakov Ellis

Odpowiedzi:

212

Swoją drogą faceci, (int) Decimal.MaxValue się przepełni. Nie możesz uzyskać części „int” ułamka dziesiętnego, ponieważ jest on zbyt duży, aby można go było umieścić w polu int. Właśnie sprawdzone ... jest nawet za duży na długi (Int64).

Jeśli chcesz, aby bit wartości Decimal znajdował się po LEWEJ STRONIE kropki, musisz to zrobić:

Math.Truncate(number)

i zwróć wartość jako ... A DECIMAL lub DOUBLE.

edit: Truncate to zdecydowanie właściwa funkcja!


źródło
Więc wynik jest dziesiętny lub podwójny, który nigdy nie będzie miał nic po przecinku, ale nie ma wbudowanego obiektu do przechowywania wyniku jako „int” (bez miejsc dziesiętnych), który wydaje się nieco kiepski?
Coops
@CodeBlend: Nie ma potrzeby projektowania ram wokół chęci utraty precyzji.
@CodeBlend: Nadal tracisz precyzję, ponieważ odcinasz wartości dziesiętne liczby. Nie jestem pewien, do czego zmierzasz.
Nie jestem pewien, czy to zadziała, czy nie. Ponieważ Math.Truncate (-5.99999999999999999) zwraca dla mnie -6,0 ... !!
Bharat Mori
@Bharat Mori: Wygląda na to, że -5.99999999999999999 jest zaokrąglane do -6,0 przed obcięciem. Spróbuj z przyrostkiem „m” i zadziała. Math.Truncate (-5.99999999999999999m) daje -5.
Henry
4

Zależy od tego, co robisz.

Na przykład:

//bankers' rounding - midpoint goes to nearest even
GetIntPart(2.5) >> 2
GetIntPart(5.5) >> 6
GetIntPart(-6.5) >> -6

lub

//arithmetic rounding - midpoint goes away from zero
GetIntPart(2.5) >> 3
GetIntPart(5.5) >> 6
GetIntPart(-6.5) >> -7

Domyślnie jest zawsze to pierwsze, co może być zaskoczeniem, ale ma bardzo dobry sens .

Twoja wyraźna obsada zrobi:

int intPart = (int)343564564.5
// intPart will be 343564564

int intPart = (int)343564565.5
// intPart will be 343564566

Ze sposobu, w jaki sformułowałeś pytanie, wygląda na to, że to nie jest to, czego chcesz - za każdym razem chcesz je rozwiązać.

Chciałbym zrobić:

Math.Floor(Math.Abs(number));

Sprawdź również swój rozmiar decimal- mogą być dość duże, więc może być konieczne użycie pliku long.

Keith
źródło
(long) Przepełnienia Decimal.MaxValue.
Przeciętny punkt - myślę, że dlatego Math.Truncate (decimal) zwraca wartość dziesiętną.
Keith
W C # rzutowanie na int nie zaokrągla się, więc (int) 0.6f będzie równe 0, a (int) 343564565.5 zakończy się na 5, a nie 6. Spróbuj tutaj: repl.it/repls/LuxuriousCheerfulDistributeddatabase
sschmidTU
0

Wystarczy go rzucić, jako taki:

int intPart = (int)343564564.4342

Jeśli nadal chcesz używać go jako ułamka dziesiętnego w późniejszych obliczeniach, to Math.Truncate (lub prawdopodobnie Math.Floor, jeśli chcesz zachować określone zachowanie dla liczb ujemnych) jest funkcją, której potrzebujesz.

Noldorin
źródło
5
To źle źle źle. Jeśli wynik jest większy niż to, co może pomieścić Int32, wyrzuci wyjątek lub (co gorsza !!!) po cichu przepełni i zawinie się, dając całkowicie niepoprawny wynik, nawet o tym nie wiedząc.
2
Nie, to nie jest złe . Wiele z nich nie jest poprawnych dla bardzo dużych liczb dziesiętnych / zmiennoprzecinkowych, ale w większości sytuacji jest w porządku. Liczby są bardzo często ograniczone, aby były wystarczająco niskie podczas kodowania, więc nie musi to stanowić problemu. Udostępniłem również rozwiązanie Math.Truncate, które działa dla wszystkich wartości.
Noldorin
2
Rozumiem, dlaczego jesteś na mnie wkurzony. Faktem jest, że twoja odpowiedź jest błędna. Mówisz mu, żeby zaryzykował, żeby to się nie zepsuło, ponieważ, hej, wiele liczb jest małych. To głupie ryzyko. Powinieneś zmienić swoją odpowiedź i usunąć wszystko oprócz Math.Truncate jako jedynej poprawnej części.
1
Wiesz, co mówią o ASSUME. Twoje założenie jest szczególnie okropne. Czy to jest zapalne? Myślę, że można tak powiedzieć. Można również powiedzieć, że mówienie komuś, aby zrobił coś głupiego, co spowoduje problemy na dalszej drodze, jest również zapalne, jeśli nie wręcz nieetyczne.
1
W istocie byłoby to złe, gdybym miał zamiar udzielić mylącej odpowiedzi. Tak jakby po prostu starałem się pomóc. Jeśli jestem winny nieznacznego nieporozumienia lub nie w pełni doceniam pytanie, to wystarczy - to nie jest przestępstwo. Dlaczego więc teraz się kłócimy? Wszyscy zgadzamy się, że Truncate to właściwa odpowiedź.
Noldorin
0

Bardzo łatwy sposób na oddzielenie wartości od jej części ułamkowej.

double  d = 3.5;
int i = (int)d;
string s = d.ToString();
s = s.Replace(i + ".", "");

s jest częścią ułamkową = 5, a
i jest wartością całkowitą = 3

Amit Gohel
źródło
1
Zobacz najlepszą odpowiedź powyżej. To nie działa, ponieważ (int)Decimal.MaxValuesię przepełni.
Yaakov Ellis
0

Mam nadzieję, że ci pomogę.

/// <summary>
/// Get the integer part of any decimal number passed trough a string 
/// </summary>
/// <param name="decimalNumber">String passed</param>
/// <returns>teh integer part , 0 in case of error</returns>
private int GetIntPart(String decimalNumber)
{
    if(!Decimal.TryParse(decimalNumber, NumberStyles.Any , new CultureInfo("en-US"), out decimal dn))
    {
        MessageBox.Show("String " + decimalNumber + " is not in corret format", "GetIntPart", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return default(int);
    } 

    return Convert.ToInt32(Decimal.Truncate(dn));
}
luka
źródło
-2
   Public Function getWholeNumber(number As Decimal) As Integer
    Dim round = Math.Round(number, 0)
    If round > number Then
        Return round - 1
    Else
        Return round
    End If
End Function
Mattheu Norwood
źródło
1
Przyjęta odpowiedź - opublikowana ponad osiem lat temu - wyjaśnia, dlaczego jest ona błędna. Zasięg decimaljest znacznie większy niż zakres int. Nie jest to również kwestia VB.
Joe Farrell
@JoeFarrell - jeśli uważasz, że odpowiedź jest błędna, możesz rozważyć odrzucenie jej zamiast pozostawienia komentarza na temat efektu. Nie oznaczaj tego jednak (nie mówię, że masz). To próba odpowiedzi na pytanie. Może być w złym języku, może się mylić nawet w VB, ale to jest próba. Zobacz na przykład: „Czy jeśli odpowiedź jest odpowiedzią na niewłaściwe pytanie, to nie jest odpowiedzią? ”.
Wai Ha Lee