Intorno alla corda


10

Alcuni numeri decimali non possono essere rappresentati con precisione come float binari a causa della rappresentazione interna dei float binari. Ad esempio: arrotondando da 14.225 a due cifre decimali non si ottiene 14.23 come ci si potrebbe aspettare ma in 14.22.

Python :

In: round(14.225, 2)
Out: 14.22

Supponiamo, tuttavia, che abbiamo una rappresentazione di stringa di 14.225 come '14 .225 ', dovremmo essere in grado di ottenere il nostro arrotondamento desiderato '14 .23' come rappresentazione di stringa.

Questo approccio può essere generalizzato a precisione arbitraria.

Possibile soluzione 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

Provalo online!

Utilizzo :

     # 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

Compito

Argomento di input 1 : una stringa contenente

  • almeno una cifra ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
  • al massimo un punto decimale ( .) che deve essere preceduto da almeno una cifra,
  • un segno meno ( -) opzionale come primo carattere.

Argomento di input 2 : un numero intero non negativo

Output : la stringa correttamente arrotondata (base 10)

arrotondamento = arrotondamento a metà distanza da zero

Questo è un . Vince il numero più basso di byte!


@KevinCruijssen 1) Non è necessario attenersi alle stringhe nel corpo dell'implementazione e è consentito utilizzare l'arrotondamento incorporato. Sfortunatamente (per la domanda) lo standard IEEE 754 è uno standard ampiamente utilizzato e quindi l'arrotondamento incorporato non comporterà il comportamento desiderato. 2) Ok, non ero a conoscenza della sandbox.
Matthias,

TI-Basic: round(A,B5 byte
Julian Lachniet

1
Per quanto riguarda il secondo argomento di input: 0non è un numero intero positivo, è "non negativo".
Stewie Griffin,

1
Presumo che aggiungeremo zeri finali se necessario? Potresti forse aggiungere un caso di prova per 123.4 & 5 --> 123.40000? Oppure possiamo supporre che il secondo input non sarà mai maggiore della quantità di decimali dopo il punto nel primo input?
Kevin Cruijssen,

1
@Matthias A meno che tu non possa integrare Python con JavaScript (non ho mai programmato Python e a malapena JS, quindi onestamente non so se sia possibile) no. Ma puoi sempre aggiungere un link Provalo online con il tuo codice di prova. EDIT: Inoltre, di solito è meglio aspettare almeno un paio di giorni fino ad accettare una risposta.
Kevin Cruijssen,

Risposte:



5

Perl, 22 20 byte

printf"%.*f",pop,pop

usando:

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

È la versione del codice di Dada. Precedente:

printf"%*2\$.*f",@ARGV

2
printf"%.*f",pop,popdovrebbe funzionare
Dada,

5

PHP, 33 31 byte

Anche PHP viene arrotondato correttamente (almeno a 64 bit):

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

accetta input dagli argomenti della riga di comando. Corri con -r.

PHP, nessun built-in, 133 byte

[,$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;

Esegui -nro testalo online .

abbattersi

[,$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;

Un byte null non funziona; quindi devo usare substr.


1
È possibile scrivere "%.$argv[2]f"invece di "%.{$argv[2]}f", salvando 2 byte.
Ismael Miguel,

4

Rubino 2.3, 12 + 45 = 57

Utilizza il BigDecimalbuilt-in, ma deve essere richiesto prima dell'uso, che è più economico da fare come bandiera.

la bandiera: -rbigdecimal

la funzione:

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

Ruby 2.3 per impostazione predefinita ROUND_HALF_UP


4

Javascript (ES6), 44 byte

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

Provalo 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))


4

Python, 114 105 103 96 91 89 byte

Salvataggio di 5 byte grazie a Kevin Cruijssen Salvataggio di
2 byte grazie a Krazor

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

Provalo online!


1
from decimal import *e rimuovendo i tre d.è più corto di 4 byte.
Kevin Cruijssen,

@KevinCruijssen: grazie!
Emigna,

2
Potresti anche fare d=Decimale d() , il che salverebbe un altro 5. (Potrebbe essere sbagliato, molto assonnato)
FMaz

@Krazor: A meno che non abbia sbagliato, mi ha salvato 2 byte. Grazie!
Emigna,

Woops, questo è quello che intendevo. Lascerò comunque i miei pensieri assonnati.
FMaz,


3

BASH, 26 23 21 byte

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

uso

salva in round_string.sh, chmod + x round_string.sh

./round_string.sh 23456789.987654321 3

modifica: non è necessario caricare la libreria


Spiegazione: bc usa la precarico arbitraria, crea un documento here con '<<<' che contrappone il valore di scala come secondo parametro e il primo parametro diviso 1 per forzare l'interpretazione della scala.
marcosm,

2
Questo dà 14.22l'ingresso 14.225 2, e non14.23
Digital Trauma

3

AHK, 25 byte

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

Ancora una volta sono sventato dall'incapacità di AHK di utilizzare parametri passati direttamente in funzioni che accettano un nome di variabile o un numero. Se lo sostituisco acon 1nella Roundfunzione, utilizza il valore 1. Se provo %1%, cerca di usare il contenuto del primo argomento come nome di variabile, che non funziona. Doverlo impostare come un'altra variabile mi è costato 6 byte.


3

Lotto, 390 byte

@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%%

Spiegazione. Inizia estraendo il segno, se applicabile. Quindi, divide il numero in cifre intere e di frazione. La frazione è riempita con n+1zeri per assicurarsi che abbia più di ncifre. La ncifra th (zero-indexed) è divisa per 5, e questo è il carry iniziale. Le ncifre di numeri interi e frazioni vengono concatenate e il carattere aggiuntivo viene aggiunto per carattere. (Gli extra zeri guardano dall'increspatura del carry.) Dopo che il carry smette di incresparsi, il numero viene ricostruito e ogni punto decimale inserito.


3

TI-Basic, 53 16 byte

TI-Basic non utilizza IEEE e il metodo seguente funziona per posizioni decimali 0-9 (incluse).

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

Grazie a @JulianLachniet per aver dimostrato che i calc CE hanno il toString( comando di cui non ero a conoscenza (sono richiesti i Color OS calc 5.2 o successivi).

PS Avevo una seconda linea con sub(Str1,1,N+inString(Str1,".ma poi mi sono reso conto che era inutile.


Come si Nusa
Matthias,

@Matthias Grazie per aver colto quel refuso! Ho rimosso per errore gli ultimi tre byte con la mia modifica precedente
Timtech,

3

Java 7, 77 72 71 byte

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

-1 byte grazie a @cliffroot

Risposta a 72 byte:

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

A differenza di Python, Java è già arrotondato correttamente e restituisce già una stringa quando si utilizza String.format("%.2f", aDouble)con2 sostituito con la quantità di decimali desiderati.

EDIT / NOTA: Sì, sono consapevole che new Float(n)è 1 byte più corto di new Double(n), ma a quanto pare non riesce per i casi di test con 123456789.987654321. Vedi questo codice di prova per Double vs Float.

Spiegazione:

<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

Codice di prova:

Provalo qui.

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));
    }
  }
}

Produzione:

14.23
123.40000
100
-100
1
1
1
123456790
123456790.0
123456789.99
123456789.988
123456789.9877
123456789.98765
123456789.987654
123456789.9876543

1
Un byte più corto:<T>T c(T n,int d){return(T)"".format("%."+d+"f",new Double(n+""));}
cliffroot

2
Questa soluzione non funziona . Sebbene l'esempio sia potenzialmente un problema a metà pari / assente 0, si verificano errori in virgola mobile e da allora OP ha chiarito che la precisione arbitraria dovrebbe essere supportata.
CAD97

1
In effetti, fallisci i casi di esempio nella domanda che hai riprodotto qui: 123456789.987654321, 4dovrebbe essere 123456789.9877, non123456789.9876
CAD97

2

Python (2/3), 394 byte

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))

Funziona con numeri di precisione arbitrari.


5
Ehi, e benvenuto in PPCG! Tuttavia, questo non è golf. C'è molto spazio bianco che puoi rimuovere. Le risposte su questo sito devono essere giocate a golf, mi dispiace.
Rɪᴋᴇʀ

Solo alcune cose (probabilmente c'è molto di più) ... Il nome della funzione può essere un byte. La prima linea può utilizzare s[0]<'0'e potrebbe anche usare stringa moltiplicazione m='-'*(s[0]<'0'). Le linee senza alcun intervallo di istruzione di blocco possono essere unite ;(ad es o='';c=0.). Alcune ifistruzioni potrebbero essere probabilmente sostituite dall'indicizzazione di elenchi per ridurre ulteriormente la necessità di interruzioni di riga e schede. L'ultima riga potrebbe usare una sezione o[::-1], invece di reversed(o)ed ''.joinè ridondante. Potresti anche essere in grado di riscriverlo per evitare la necessità di più returndichiarazioni.
Jonathan Allan,

2
... se sei interessato, ci sono consigli per giocare a golf in Python qui .
Jonathan Allan,

2

JavaScript (ES6), 155 byte

(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)

Spiegazione: La stringa viene prima normalizzato per contenere una .e n+1decimali cifre. La cifra di trascinamento, qualsiasi precedente 9s o .s e ogni cifra precedente, vengono considerati. Se l'ultima cifra è inferiore a 5, .viene semplicemente rimossa e qualsiasi precedente immediatamente, ma se è maggiore di 5, le 9s vengono modificate in se 0la cifra precedente viene incrementata (o 1 con prefisso se non era presente alcuna cifra precedente).



1

Scala, 44 byte

(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

1

Meraviglia , 10 byte

@@fix#1E#0

Uso:

@@fix#1E#0

Imposta la precisione decimale e aggiungi zeri finali, se necessario.


C'è un TIO per questo?
Matthias,

No non c'è, ma l'installazione è abbastanza semplice. Assicurati di avere Node.js (v6 +) e npm i -g wonderlang. Usa il wondercomando per accendere il REPL e incolla il codice.
Mama Fun Roll

1

J, 22 17 byte

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

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

Grazie a @Conor O'Brien per aver corretto la mia comprensione delle regole.

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.

La sfida richiede di prendere il numero di cifre dopo il punto decimale per arrotondare a N decimali, non a N punti di precisione. Come tale, 2 t '1234.456'dovrebbe dare 1234.46invece di6 t '1234.456'
Conor O'Brien il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.