Satysfakcjonujące zaokrąglanie

16

Satysfakcjonujące zaokrąglanie

Wiesz, kiedy jesteś w klasie naukowej i poprosiłeś o zaokrąglenie do 2 sig fig, ale twoja odpowiedź brzmi 5.2501...? Powinieneś zaokrąglić do 5.3, ale to po prostu takie niezadowalające! Zaokrąglając do 5.3, otrzymujesz całe 0,05, co stanowi dużą kwotę w porównaniu do 0,1 (wartość miejsca, do której zaokrąglasz)! Pomóż mi więc w satysfakcjonujący sposób.

Aby zaokrąglić w sposób satysfakcjonujący, należy zaokrąglić pierwszą napotkaną cyfrę, która powoduje stosunkowo mały błąd - mniej niż połowę maksymalnego błędu możliwego podczas zaokrąglania. Zasadniczo musisz zaokrąglać za każdym razem, gdy napotkasz 0, 1, 8 lub 9. Jeśli to się nigdy nie zdarzy, zwróć dane wejściowe bez zmian. Nie zaokrąglaj wiodących zer lub jedynek - to po prostu nie jest satysfakcjonujące.

Wejście

Ciąg lub wartość zmiennoprzecinkowa reprezentująca nieujemną liczbę dziesiętną.

Wynik

Ta sama liczba dziesiętna zaokrąglona w sposób zadowalający, zarówno w formacie ciągowym, jak i zmiennoprzecinkowym.

Przykłady

Input -> Output
0 -> 0
0.5 -> 0.5
0.19 -> 0
0.8 -> 1
5.64511 -> 5.645
18.913 -> 20
88.913 -> 100
36.38299 -> 36.4
621 -> 620
803.22 -> 1000
547.4726 -> 547.4726

To wyzwanie dla , więc wygrywa najkrótszy kod!

Quintec
źródło
Piaskownica
Quintec
Czy ciągi, takie jak 036.40000uważane za prawidłowe dane wyjściowe?
Arnauld,
1
Czy możemy założyć, że .0część zostanie podana dla liczb całkowitych? Również 0nie jest pozytywne.
Erik the Outgolfer,
@EriktheOutgolfer Nie, możesz nie - także dzięki, zmieniono na nieujemny.
Quintec
1
Więc 19zaokrągla do, 20ale 0.19zaokrągla do 0? Dlaczego?
Neil,

Odpowiedzi:

2

JavaScript (ES6),  100 99 98  78 bajtów

Pobiera dane wejściowe jako ciąg. Zwraca liczbę zmiennoprzecinkową.

s=>+(0+s).replace(/\d/g,(d,i)=>j&&+d+((n=s[i+!++s[i]])<2&&i?--j:n>7&&j--),j=1)

Wypróbuj online!

W jaki sposób?

Najpierw wstawiamy wiodące 0 do ciągu wejściowego, aby zagwarantować, że będziemy mieć cyfrę przed możliwym początkowym 8 lub 9 , która musi natychmiast uruchomić zaokrąglanie.

Flaga j jest ustawiona na 1 dopóki szukamy cyfry, na której możemy wykonać satysfakcjonujące zaokrąglenie, i ustawiona na0 .

Ponieważ wiodące 0 zostało dodane do ciągu, przez który przechodzimy, ale s pozostało niezmienione, d zawiera bieżący znak s[i] wskazuje na następny znak.

Używamy następującego kodu, aby załadować następną cyfrę w n , pomijając ewentualny separator dziesiętny:

n = s[i + !++s[i]]

Chociaż ciągi znaków są niezmienne w JavaScript, wyrażenie ++s[i]zwróci s[i]+1 jeśli zawiera wartość liczbową, nawet jeśli s[i] nie jest w rzeczywistości zwiększany. Dlatego wyrażenie !++s[i]jest oceniane na false (wymuszone na 0 ) dla wszystkich cyfr (w tym 0 ) i na true (wymuszone na 1 ) dla separatora dziesiętnego"." .

Kiedy następuje zaokrąglenie, dajemy, d + --jjeśli następna cyfra n wynosi 0 lub 1 (i nie jest to cyfra wiodąca oryginalnego wejścia) i d + j--jeśli n wynosi 8 lub 9 . Dlatego j jest ustawione na 0 w obu przypadkach, ale dodajemy 0 do d w pierwszym przypadku (zaokrąglanie w dół) i 1 w drugim przypadku (zaokrąglanie w górę).

Arnauld
źródło
1
A pinball / gumowa piłka wpada do rowu! :)
Quintec
2

Ruby , 79 77 69 67 65 bajtów

->n,z=n+".0"{z[i=z=~/\./]='';n.to_f.round (z=~/(?!^)[01]|8|9/)-i}

Wypróbuj online!

Wyjaśnienie

  • ->n Weź dane jako ciąg
  • z=n+".0" Utwórz tymczasowy ciąg z który ma gwarantowaną kropkę i odpowiednią cyfrę.
  • i=z=~/\./Określ pozycję kropki dziesiętnej zi przypisz doi .
  • z[i]='' Upuść kropkę, aby nie przeszkadzała.
  • z=~/(?!^)[01]|8|9/Określenie pozycji nie wychodząc 0-1lub dowolny8-9 , w zależności od tego, co nastąpi wcześniej.
  • (...)-i Ta różnica będzie liczbą miejsc dziesiętnych, które należy zachować, ujemną, jeśli będziemy zaokrąglać po lewej stronie kropki.
  • n.to_f.round ... Konwertuj na float i wykonaj zaokrąglanie.
Kirill L.
źródło
1

Galaretka , 34 bajty

;”.ḟ$µ»"”2e€⁽¡XṾ¤;1i1_i”.$_>¥0ɓVær

Wypróbuj online!

-1 dzięki Jonathanowi Allanowi .

Erik the Outgolfer
źródło
Dlaczego ŒV? Myślę, że Vteż zadziała.
Jonathan Allan,
@JonathanAllan Nope. (w zasadzie dziwactwa zaokrągleń bankiera)
Erik the Outgolfer
Och, bo to nie działa na wejściu? Spróbuj _>¥0ɓVærjak moje (brakowało mi używania dynamicznego, więc też dzięki!)
Jonathan Allan
@JonathanAllan Ah, sprytne użycie łańcuchów, dzięki.
Erik the Outgolfer,
1

Galaretka ,  30  29 bajtów

-1 dzięki Erikowi Outgolfer (użycie diademu szybko ¥z jego odpowiedzi)

O;0µ_8H1¦%8ỊTḢ_<48TḢƊ_>¥0ɓVær

Monadyczny link akceptujący listę znaków, który daje liczbę zmiennoprzecinkową.

Wypróbuj online! Lub zobacz pakiet testowy .

W jaki sposób

Po pierwsze zauważ, że łańcuch wejściowy składa się wyłącznie z znaków, 0123456789.które mają liczby porządkowe [48,49,50,51,52,53,54,55,56,57,46], które mają reszty po podzieleniu przez osiem z [0,1,2,3,4,5,6,7,0,1,6]. Jedyne znaki, które są pomiędzy -1i 1włącznie są 0, 1, 8, i 9.

Ponadto, jeśli odejmiemy osiem od rzędnych ( [40,41,42,43,44,45,46,47,48,49,38]), zachowa się to samo (dość oczywiste). Jeśli [20,20.5,21,21.5,22,22.5,23,23.5,24,24.5,19]podzielimy je na pół ( ), jedynymi znakami, które mają reszty, gdy zostaną podzielone przez osiem, które są pomiędzy -1i 1włącznie, są 8i 9.

O;0µ_8H1¦%8ỊTḢ_<48TḢƊ_>¥0ɓVær - Link: list of characters, S
O                             - ordinal (vectorises across S)
 ;0                           - concatenate a zero
                              - (to cater BOTH for no '0', '1', '8', or '9' AND for no '.')
   µ                          - start a new monadic link (call that X)
    _8                        - subtract eight (vectorises across X)
        ¦                     - sparse application...
       1                      - ...to: indices: one
      H                       - ...do: halve (i.e. halve first ordinal)
         %8                   - modulo by eight (vectorises)
           Ị                  - insignificant (abs(v)<=1?) (vectorises)
            T                 - truthy indices
             Ḣ                - head
                    Ɗ         - last three links as a monad (i.e. f(X)):
               <48            -   less than 48? (i.e. was it a '.' in S or the added 0?)
                  T           -   truthy indices
                   Ḣ          -   head
              _               - subtract
                       ¥      - last two links as a dyad
                      < 0     -   less than zero? (1 if so 0 otherwise)
                     _        -   subtract
                         ɓ    - start a new dyadic chain (i.e. f(S,X))
                          V   - evaluate S as Jelly code (i.e. get S as a float)
                           ær - round to the nearest multiple of 10^(-X)
Jonathan Allan
źródło
1

Retina 0.8.2 , 75 bajtów

^[89]
10
T`d`0`(?<=.)[01].*|(?<=8|9).*
T`89d`0d`.\.?[89]
(\.|(\..+?))0+$
$2

Wypróbuj online! Link zawiera przypadki testowe. Wyjaśnienie:

^[89]
10

Obsługiwać przypadek wiodącego 8lub 9.

T`d`0`(?<=.)[01].*|(?<=8|9).*

Jeśli jest nieprzewodzący 0lub 1, wyzeruj go i resztę ciągu. Ponadto, jeśli istnieje znak „ 8lub” 9, pozostaw go, ale wyzeruj resztę ciągu. (Ale w każdym przypadku pozostaw kropkę dziesiętną bez zmian).

T`89d`0d`.\.?[89]

Jeśli w tym miejscu nadal jest cyfra 8lub a 9, wyzeruj ją i zwiększ poprzednią cyfrę (być może przed kropką dziesiętną).

(\.|(\..+?))0+$
$2

Usuń końcowe zera, jeśli występują po przecinku, ale usuń przecinek tylko wtedy, gdy nie ma między nimi żadnych innych cyfr.

Neil
źródło
1

C (gcc) , 111 102 bajtów

g(_,i,j,k)char*_;{for(i=*_<56?*_++:48,j=3;j;j&=k%8>1|(i=*_++)/48*2)putchar(j&1?i+(k=_[*_<48])/56:48);}

Wypróbuj online!

//_: input, as string
//i: current digit, rounded if applicable
//j: tracks whether number is rounded, and whether \0 or '.' has been encountered
//k: digit to test rounding (round if k is one of 0,1,8,9)
//'0'==48, '8'==56
g(_,i,j,k)char*_;{
    for(i=*_<56?*_++:48,j=3;                //special case: if first digit is 8 or 9, use a
                                            //placeholder digit with value 0. initialize j.
        j;                                  //only stop execution when number is rounded and
                                            //'.' or \0 has been encountered.
        j&=k%8>1|(i=*_++)/48*2)             //check if execution should stop.
        putchar(j&1?i+(k=_[*_<48])/56:48);  //print '0' if rounding had already been done;
                                            //otherwise, print digit. round up as needed.
}
attinat
źródło
0

C # (interaktywny kompilator Visual C #) , 280 bajtów

c=>{int d=c.IndexOf('.');int t=c.IndexOfAny(new char[]{'8','9','0','1'},1);var m=c[0]=='8'||c[0]=='9'?1>0:0>1;var z=decimal.Parse(c);Func<decimal>q=()=>(decimal)Math.Pow(10,m?d<0?c.Length:d:d<0?c.Length-t:d>t?d-t:d-t+1);return m?Math.Round(z/q())*q():t<0?z:Math.Round(z/q())*q();}

Wypróbuj online!

Może być krótszy, jeśli użyłem podwójnych zamiast dziesiętnych, ale użyłem dziesiętnych dla zachowania dokładności, w przeciwnym razie liczba taka jak 547,4726 byłaby 547.472595214844.

C # (interaktywny kompilator Visual C #) , 268 bajtów

c=>{int d=c.IndexOf('.');int t=c.IndexOfAny(new char[]{'8','9','0','1'},1);var m=c[0]=='8'||c[0]=='9'?1>0:0>1;var z=float.Parse(c);Func<double>q=()=>Math.Pow(10,m?d<0?c.Length:d:d<0?c.Length-t:d>t?d-t:d-t+1);return m?Math.Round(z/q())*q():t<0?z:Math.Round(z/q())*q();}

Wypróbuj online! (Mniej dokładna wersja)

Wcielenie ignorancji
źródło