Zaokrąglij sznurek

10

Niektórych liczb dziesiętnych nie można dokładnie przedstawić jako liczb binarnych ze względu na wewnętrzną reprezentację liczb binarnych. Na przykład: zaokrąglenie 14,225 do dwóch cyfr dziesiętnych nie daje 14,23, jak można się spodziewać, ale 14,22.

Python :

In: round(14.225, 2)
Out: 14.22

Załóżmy jednak, że mamy ciąg znaków reprezentujący 14,225 jako „14,225”, powinniśmy być w stanie osiągnąć pożądane zaokrąglenie „14,23” jako ciąg znaków.

To podejście można uogólnić z dowolną precyzją.

Możliwe rozwiązanie Python 2/3

import sys

def round_string(string, precision):
    assert(int(precision) >= 0)
    float(string)

    decimal_point = string.find('.')
    if decimal_point == -1:
        if precision == 0:
            return string
        return string + '.' + '0' * precision

    all_decimals = string[decimal_point+1:]
    nb_missing_decimals = precision - len(all_decimals)
    if nb_missing_decimals >= 0:
        if precision == 0:
            return string[:decimal_point]
        return string + '0' * nb_missing_decimals

    if int(all_decimals[precision]) < 5:
        if precision == 0:
            return string[:decimal_point]
        return string[:decimal_point+precision+1]

    sign = '-' if string[0] == '-' else '' 
    integer_part = abs(int(string[:decimal_point]))
    if precision == 0:
        return sign + str(integer_part + 1)
    decimals = str(int(all_decimals[:precision]) + 1)
    nb_missing_decimals = precision - len(decimals)
    if nb_missing_decimals >= 0:
        return sign + str(integer_part) + '.' + '0' * nb_missing_decimals + decimals
    return sign + str(integer_part + 1) + '.' + '0' * precision

Wypróbuj online!

Zastosowanie :

     # No IEEE 754 format rounding
In:  round_string('14.225',2)
Out: '14.23'

     # Trailing zeros
In:  round_string('123.4',5)
Out: '123.40000'

In: round_string('99.9',0)
Out: '100'

    # Negative values
In: round_string('-99.9',0)
Out: '-100'

In: round_string('1',0)
Out: '1'

    # No unnecessary decimal point
In: round_string('1.',0)
Out: '1'

    # No unnecessary decimal point
In: round_string('1.0',0)
Out: '1'

In:  for i in range(8): 
         print(round_string('123456789.987654321',i))
Out: 123456790
     123456790.0
     123456789.99
     123456789.988
     123456789.9877
     123456789.98765
     123456789.987654
     123456789.9876543

Zadanie

Argument wejściowy 1 : ciąg zawierający

  • co najmniej jedna cyfra ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
  • co najwyżej jedna kropka dziesiętna ( .), która musi być poprzedzona co najmniej jedną cyfrą,
  • opcjonalny minus ( -) jako pierwszy znak.

Argument wejściowy 2 : nieujemna liczba całkowita

Wyjście : poprawnie zaokrąglony ciąg (podstawa 10)

zaokrąglenie = zaokrąglić w połowie od zera

To jest . Najmniejsza liczba bajtów wygrywa!

Matthias
źródło
@KevinCruijssen 1) Nie musisz trzymać się ciągów znaków w treści swojej implementacji i możesz używać wbudowanego zaokrąglania. Niestety (w przypadku pytania) standard IEEE 754 jest powszechnie stosowanym standardem, a zatem wbudowane zaokrąglanie nie spowoduje pożądanego zachowania. 2) Ok, nie wiedziałem o piaskownicy.
Matthias
TI-Basic: round(A,B5 bajtów
Julian Lachniet
1
Jeśli chodzi o drugi argument wejściowy: 0nie jest dodatnią liczbą całkowitą, jest „nieujemny”.
Stewie Griffin,
1
Zakładam, że w razie potrzeby dodamy końcowe zera? Czy mógłbyś dodać przypadek testowy 123.4 & 5 --> 123.40000? Czy możemy założyć, że drugie wejście nigdy nie będzie większe niż liczba miejsc po przecinku po punkcie na pierwszym wejściu?
Kevin Cruijssen
1
@Matthias O ile nie możesz zintegrować Pythona z JavaScriptem (nigdy nie programowałem Pythona i tylko JS, więc szczerze mówiąc nie wiem, czy to możliwe) nie. Ale zawsze możesz dodać link Wypróbuj online ze swoim kodem testowym. EDYCJA: Zazwyczaj lepiej jest poczekać przynajmniej kilka dni, aż zaakceptujesz odpowiedź.
Kevin Cruijssen

Odpowiedzi:

2

APL (Dyalog) , 4 bajty

Dyalog APL wykorzystuje wystarczającą precyzję wewnętrzną.

⎕⍕⍎⍞

Wypróbuj online!

⍎⍞ wykonać ciąg znaków

⎕⍕ uzyskaj dane numeryczne i użyj tego jako precyzji formatowania

Adám
źródło
Dlaczego to 4 zamiast 8 bajtów?
Matthias
@ Matthias Ponieważ Dyalog APL ma SBCS
Adám
5

Perl, 22 20 bajtów

printf"%.*f",pop,pop

Za pomocą:

perl -e 'printf"%.*f",pop,pop' 123456789.987654321 3

Jest to wersja kodu Dady. Poprzedni:

printf"%*2\$.*f",@ARGV
Denis Ibaev
źródło
2
printf"%.*f",pop,poppowinien działać
Dada,
5

PHP, 33 31 bajtów

PHP też zaokrągla poprawnie (przynajmniej w wersji 64-bitowej):

printf("%.$argv[2]f",$argv[1]);

pobiera dane wejściowe z argumentów wiersza poleceń. Uruchom z -r.

PHP, brak wbudowanych, 133 bajtów

[,$n,$r]=$argv;if($p=strpos(_.$n,46))for($d=$n[$p+=$r],$n=substr($n,0,$p-!$r);$d>4;$n[$p]=(5+$d=$n[$p]-4)%10)$p-=$n[--$p]<"/";echo$n;

Uruchom go -nrlub przetestuj online .

awaria

[,$n,$r]=$argv;             // import arguments
if($p=strpos(_.$n,46))      // if number contains dot
    for($d=$n[$p+=$r],          // 1. $d= ($r+1)-th decimal 
        $n=substr($n,0,$p-!$r); // 2. cut everything behind $r-th decimal
        $d>4;                   // 3. loop while previous decimal needs increment
        $n[$p]=(5+$d=$n[$p]-4)%10   // B. $d=current digit-4, increment current digit
    )
        $p-=$n[--$p]<"/";           // A. move cursor left, skip dot
echo$n;

Bajt zerowy nie działa; więc muszę użyć substr.

Tytus
źródło
1
Możesz pisać "%.$argv[2]f"zamiast "%.{$argv[2]}f", oszczędzając 2 bajty.
Ismael Miguel
4

Ruby 2.3, 12 + 45 = 57

Korzysta z BigDecimalwbudowanego, ale musi być wymagany przed użyciem, co jest tańsze jako flaga.

Flaga: -rbigdecimal

funkcja:

->(s,i){BigDecimal.new(s).round(i).to_s('f')}

Domyślnie używa Ruby 2.3 ROUND_HALF_UP

Sztuczki
źródło
4

JavaScript (ES6), 44 bajty

n=>p=>(Math.round(n*10**p)/10**p).toFixed(p)

Wypróbuj online:

const f = n=>p=>(Math.round(n*10**p)/10**p).toFixed(p)

console.log(f('14.225')(2));

[...Array(8).keys()].map(i=>console.log(f('123456789.987654321')(i)))

console.log(f('123.4')(5))

powelles
źródło
4

Python, 114 105 103 96 91 89 bajtów

Zaoszczędź 5 bajtów dzięki Kevinowi Cruijssenowi
Zaoszczędź 2 bajty dzięki Krazorowi

from decimal import*
d=Decimal
lambda x,y:d(x).quantize(d('0.'[y>0]+'1'*y),ROUND_HALF_UP)

Wypróbuj online!

Emigna
źródło
1
from decimal import *a usunięcie tych trzech d.jest o 4 bajty krótsze.
Kevin Cruijssen
@KevinCruijssen: Dzięki!
Emigna,
2
Mógłbyś także zrobić, d=Decimala d() co uratowałoby kolejne 5. (Może być źle, bardzo sennie)
FMaz
@Krazor: O ile nie zrobiłem tego źle, zaoszczędziłem 2 bajty. Dzięki!
Emigna
Ups, o to mi chodziło. I tak zostawię moje senne myśli.
FMaz
4

REXX, 24 bajty

arg n p
say format(n,,p)

Ponieważ REXX zawsze używa tekstowej reprezentacji liczb, prawidłowe zaokrąglanie liczb jest bezpłatne.

Wypróbuj online!

idrougge
źródło
Łał. miłe nadużycie języka.
Matthew Roh
Czy REXX ma kompilator online?
Kevin Cruijssen
To (głównie) język interpretowany.
idrougge,
3

BASH, 26 23 21 bajtów

bc<<<"scale=$2;$1/1"

stosowanie

zapisz w round_string.sh, chmod + x round_string.sh

./round_string.sh 23456789.987654321 3

edycja: nie trzeba ładować biblioteki

marcosm
źródło
Objaśnienie: bc używa arbitralnej prekrecji, utwórz tutaj dokument z „<<<” zawierającym wartość skali jako drugiego parametru i pierwszego parametru podzielonego przez 1, aby wymusić interpretację skali.
marcosm
2
Daje 14.22to dane wejściowe 14.225 2, a nie14.23
Digital Trauma
3

AHK, 25 bajtów

a=%1%
Send % Round(a,%2%)

Znowu jestem przytłoczony niemożnością użycia przez AHK parametrów przekazanych bezpośrednio w funkcjach, które akceptują nazwę zmiennej lub liczbę. Jeśli mogę wymienić az 1w Roundfunkcji, używa wartości 1. Gdy próbuję %1%, próbuje użyć pierwszego argumentu za treść jako nazwy zmiennej, która nie działa. Konieczność ustawienia go jako innej zmiennej najpierw kosztowała mnie 6 bajtów.

Inżynier Toast
źródło
3

Partia, 390 bajtów

@echo off
set s=%1
set m=
if %s:~,1%==- set m=-&set s=%s:~1%
set d=%s:*.=%
if %d%==%s% (set d=)else call set s=%%s:.%d%=%%
for /l %%i in (0,1,%2)do call set d=%%d%%0
call set/ac=%%d:~%2,1%%/5
call set d=00%s%%%d:~,%2%%
set z=
:l
set/ac+=%d:~-1%
set d=%d:~,-1%
if %c%==10 set c=1&set z=%z%0&goto l
set d=%m%%d:~2%%c%%z%
if %2==0 (echo %d%)else call echo %%d:~,-%2%%.%%d:~-%2%%

Wyjaśnienie. Zaczyna od wyodrębnienia znaku, jeśli dotyczy. Następnie dzieli liczbę na liczby całkowite i ułamkowe. Ułamek jest wypełniany n+1zerami, aby mieć więcej niż ncyfry. nP (zero indeksowane) cyfrowy dzieli się przez 5, a to początkowa przenoszenia. nCyfry całkowite i ułamkowe są łączone, a przeniesienie dodawane znak po znaku. (Dodatkowe zera chronią przed falowaniem przenoszenia). Po zatrzymaniu falowania przenoszenia liczba jest rekonstruowana i wstawiany jest przecinek dziesiętny.

Neil
źródło
3

TI-Basic, 53 16 bajtów

TI-Basic nie używa IEEE, a poniższa metoda działa dla 0-9 (włącznie) pozycji dziesiętnych.

Prompt Str1,N
toString(round(expr(Str1),N

Dzięki @JulianLachniet za wykazanie, że cielęta CE mają toString( polecenie, którego nie znałem (wymagana jest edycja Calcs OS 5.2 lub nowsza).

PS Miałem drugą linię, sub(Str1,1,N+inString(Str1,".ale potem zdałem sobie sprawę, że to było bezużyteczne.

Timtech
źródło
Jak Nstosować?
Matthias
@Matthias Dzięki za złapanie tej literówki! Przypadkowo usunąłem ostatnie trzy bajty z moją poprzednią edycją
Timtech
3

Java 7, 77 72 71 bajtów

<T>T c(T n,int d){return(T)"".format("%."+d+"f",new Double(n+""));}

-1 bajt dzięki @cliffroot

72-bajtowa odpowiedź:

String c(String n,int d){return n.format("%."+d+"f",new Double(n));}

W przeciwieństwie do Pythona, Java już zaokrągla poprawnie i już zwraca ciąg, gdy używasz String.format("%.2f", aDouble)z2 zastąpioną liczbą miejsc po przecinku, którą chcesz.

EDYCJA / UWAGA: Tak, wiem, że new Float(n)jest o 1 bajt krótszy niż new Double(n), ale najwyraźniej nie udaje się to z przypadkami testowymi 123456789.987654321. Zobacz ten kod testowy dotyczący Double vs Float.

Wyjaśnienie:

<T> T c(T n, int d){               // Method with generic-T & integer parameters and generic-T return-type (generic-T will be String in this case)
  return (T)"".format("%."+d+"f",  //  Return the correctly rounded output as String
    new Double(n+""));             //  After we've converted the input String to a decimal
}                                  // End of method

Kod testowy:

Wypróbuj tutaj.

class M{
  static <T>T c(T n,int d){return(T)"".format("%."+d+"f",new Double(n+""));}

  public static void main(String[] a){
    System.out.println(c("14.225", 2));
    System.out.println(c("123.4", 5));
    System.out.println(c("99.9", 0));
    System.out.println(c("-99.9", 0));
    System.out.println(c("1", 0));
    System.out.println(c("1.", 0));
    System.out.println(c("1.0", 0));
    for(int i = 0; i < 8; i++){
      System.out.println(c("123456789.987654321", i));
    }
  }
}

Wynik:

14.23
123.40000
100
-100
1
1
1
123456790
123456790.0
123456789.99
123456789.988
123456789.9877
123456789.98765
123456789.987654
123456789.9876543
Kevin Cruijssen
źródło
1
Jeden bajt krótszy:<T>T c(T n,int d){return(T)"".format("%."+d+"f",new Double(n+""));}
cliffroot
2
To rozwiązanie nie działa . Chociaż przykład ten może potencjalnie dotyczyć okrągłej połowy parzystej / braku 0, występują błędy zmiennoprzecinkowe, a OP wyjaśnił, że należy wspierać arbitralną precyzję.
97 CAD
1
W rzeczywistości zawiodłeś przykładowe przypadki w pytaniu, które tutaj odtworzyłeś: 123456789.987654321, 4powinno być 123456789.9877, nie123456789.9876
CAD97
2

Python (2/3), 394 bajty

def rnd(s,p):
    m=s[0]=='-'and'-'or''
    if m:s=s[1:]
    d=s.find('.')
    l=len(s)
    if d<0:
        if p>0:d=l;l+=1;s+='.'
        else:return m+s
    e=(d+p+1)-l
    if e>0:return m+s+'0'*e
    o=''
    c=0
    for i in range(l-1,-1,-1):
        x=s[i]
        if i<=d+p:
            if i!=d:
                n=int(x)+c
                if n>9:n=0;c=1 
                else:c=0
                o+=str(n)
            else:
                if p>0:o+=x
        if i==d+p+1:c=int(x)>4
    if c:o+='1'
    return m+''.join(reversed(o))

Działa dla dowolnych liczb dokładności.

Amol
źródło
5
Cześć, witamy w PPCG! Nie jest to jednak gra w golfa. Istnieje wiele białych znaków, które można usunąć. Niestety, odpowiedzi na tej stronie wymagają gry w golfa.
Rɪᴋᴇʀ
Tylko kilka rzeczy (prawdopodobnie jest ich znacznie więcej) ... Nazwa funkcji może być jednobajtowa. Pierwsza linia może używać s[0]<'0'i może również wykorzystywać ciąg mnożenia m='-'*(s[0]<'0'). Linie bez przedziałów instrukcji blokowych można łączyć z ;(np o='';c=0.). Niektóre ifinstrukcje można prawdopodobnie zastąpić indeksowaniem list, aby jeszcze bardziej ograniczyć potrzebę podziału wiersza i tabulacji. W ostatnim wierszu można użyć plasterka o[::-1]zamiast reversed(o)i ''.joinjest nadmiarowy. Możesz również być w stanie przepisać go, aby uniknąć konieczności używania wielu returninstrukcji.
Jonathan Allan
2
... jeśli jesteś zainteresowany, znajdziesz tutaj wskazówki dotyczące gry w golfa w Pythonie .
Jonathan Allan
2

JavaScript (ES6), 155 bajtów

(s,n)=>s.replace(/(-?\d+).?(.*)/,(m,i,d)=>i+'.'+(d+'0'.repeat(++n)).slice(0,n)).replace(/([0-8]?)([.9]*?)\.?(.)$/,(m,n,c,r)=>r>4?-~n+c.replace(/9/g,0):n+c)

Objaśnienie: Łańcuch jest najpierw znormalizowane do przechowywania .i n+1dziesiętnych cyfr. Końcowa cyfra, wszelkie poprzedzające 9s lub .s oraz dowolna poprzedzająca cyfra są następnie brane pod uwagę. Jeśli ostatnia cyfra jest mniejsza niż 5, wówczas i wszystkie bezpośrednio poprzedzające .są po prostu usuwane, ale jeśli jest większa niż 5, 9s są zmieniane na 0s, a poprzednia cyfra jest zwiększana (lub 1 prefiks, jeśli nie było poprzedniej cyfry).

Neil
źródło
1

Scala, 44 bajty

(s:String,p:Int)=>s"%.${p}f"format s.toFloat

Test:

scala> var x = (s:String,p:Int)=>s"%.${p}f"format s.toFloat
x: (String, Int) => String = <function2>

scala> x("14.225",2)
res13: String = 14.23
2xsaiko
źródło
1

Cud , 10 bajtów

@@fix#1E#0

Stosowanie:

@@fix#1E#0

Ustaw dokładność dziesiętną i w razie potrzeby dodaj końcowe zera.

Mama Fun Roll
źródło
Czy dla tego istnieje TIO?
Matthias
Nie, nie ma, ale instalacja jest dość łatwa. Upewnij się, że masz Node.js (wersja 6 +) i npm i -g wonderlang. Użyj wonderpolecenia, aby odpalić REPL i wkleić kod.
Mama Fun Roll
1

J, 22 17 bajtów

((10 j.[)]@:":".)

NB.    2    ((10 j.[)]@:":".)   '12.45678'
NB.    12.46 

Dzięki @Conor O'Brien za poprawienie mojego zrozumienia zasad.

t=:4 :'(10 j.x)":".y'

    NB.    Examples
    NB.    4 t'12.45678'
    NB.    12.4568
    NB.    4 t'12.456780'
    NB.    12.4568
    NB.    4 t'12.4567801'
    NB.    12.4568
    NB.    2 t'12.45678'
    NB.      12.46
    NB.    2 t'12.4567801'
    NB.      12.46
    NB.    2 (10 j.[)":". '_12.4567801'
    NB.     _12.46

format    
    x t y
where x is a digit number of decimal places required and y
is the character string containing the value to be rounded.
Richard Donovan
źródło
Wyzwanie wymaga podania liczby cyfr po przecinku do zaokrąglenia do N miejsc po przecinku, a nie do N punktów dokładności. Jako taki 2 t '1234.456'powinien dać 1234.46zamiast6 t '1234.456'
Conor O'Brien