Arredonde a corda


10

Alguns números decimais não podem ser representados com precisão como flutuadores binários devido à representação interna dos flutuadores binários. Por exemplo: arredondar 14,225 para dois dígitos decimais não resulta em 14,23, como seria de esperar, mas em 14,22.

Python :

In: round(14.225, 2)
Out: 14.22

Suponha, no entanto, que tenhamos uma representação de string de 14,225 como '14 .225 ', devemos conseguir o arredondamento desejado '14 .23' como uma representação de string.

Essa abordagem pode ser generalizada com precisão arbitrária.

Possível solução 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

Experimente online!

Uso :

     # 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

Tarefa

Argumento de entrada 1 : uma string contendo

  • pelo menos um dígito ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
  • no máximo um ponto decimal ( .) que deve ser precedido por pelo menos um dígito,
  • um opcional menos ( -) como primeiro caractere.

Argumento de entrada 2 : um número inteiro não negativo

Saída : a sequência corretamente arredondada (base 10)

arredondamento = Metade da rodada arredondada de zero

Este é um . O menor número de bytes vence!


@KevinCruijssen 1) Você não precisa seguir as strings no corpo da sua implementação e tem permissão para usar o arredondamento interno. Infelizmente (para a pergunta), o padrão IEEE 754 é um padrão amplamente utilizado e, portanto, o arredondamento interno não resultará no comportamento desejado. 2) Ok, não estava ciente da caixa de areia.
Matthias

TI-Basic: round(A,B5 bytes
Julian Lachniet 17/03

11
Em relação ao segundo argumento de entrada: 0não é um número inteiro positivo, é "não negativo".
Stewie Griffin

11
Suponho que adicionemos zeros à direita, se necessário? Você poderia adicionar um caso de teste 123.4 & 5 --> 123.40000? Ou podemos assumir que a segunda entrada nunca será maior que a quantidade de casas decimais após o ponto na primeira entrada?
Kevin Cruijssen

11
@ Matthias A menos que você possa integrar o Python ao JavaScript (eu nunca programei o Python e apenas o JS, então sinceramente não sei se é possível) não. Mas você sempre pode adicionar um link Experimente online com seu código de teste. EDIT: Além disso, geralmente é melhor esperar pelo menos alguns dias até você aceitar uma resposta.
21717 Kevin Murrijssen

Respostas:



5

Perl, 22 20 bytes

printf"%.*f",pop,pop

Usando:

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

É a versão do código do Dada. Anterior:

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

2
printf"%.*f",pop,popdeve trabalhar
Dada

5

PHP, 33 31 bytes

O PHP também arredonda corretamente (pelo menos em 64 bits):

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

recebe entrada dos argumentos da linha de comando. Corra com -r.

PHP, sem built-ins, 133 bytes

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

Execute -nrou teste on-line .

demolir

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

Um byte nulo não funciona; então eu tenho que usar substr.


11
Você pode escrever em "%.$argv[2]f"vez de "%.{$argv[2]}f", economizando 2 bytes.
Ismael Miguel

4

Ruby 2.3, 12 + 45 = 57

Usa o BigDecimalbuilt-in, mas precisa ser necessário antes do uso, o que é mais barato de fazer como um sinalizador.

a bandeira: -rbigdecimal

a função:

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

O Ruby 2.3 por padrão usa ROUND_HALF_UP


4

Javascript (ES6), 44 bytes

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

Experimente 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 bytes

Guardado 5 bytes graças a Kevin Cruijssen
Guardado 2 bytes graças a Krazor

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

Experimente online!


11
from decimal import *e remover os três d.é 4 bytes mais curto.
21717 Kevin Murrijssen em

@KevinCruijssen: Obrigado!
Emigna

2
Você também pode fazer d=Decimale d() , o que economizaria outros 5. (Pode estar errado, com muito sono) #
FMaz 17/17

@Krazor: A menos que eu tenha feito errado, me salvou 2 bytes. Obrigado!
Emigna

Uau, foi isso que eu quis dizer. De qualquer maneira, deixarei meus pensamentos sonolentos.
FMaz


3

BASH, 26 23 21 bytes

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

uso

salve em round_string.sh, chmod + x round_string.sh

./round_string.sh 23456789.987654321 3

editar: não há necessidade de carregar a biblioteca


Explicação: bc usa precissão arbitrária, crie um documento aqui com '<<<' contendo o valor da escala como o segundo parâmetro e o primeiro parâmetro dividido por 1 para forçar a interpretação da escala.
marcosm

2
Isso dá 14.22para a entrada 14.225 2, e não #14.23
Digital Trauma

3

AHK, 25 bytes

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

Mais uma vez, sou frustrado pela incapacidade do AHK de usar parâmetros passados ​​diretamente em funções que aceitam um nome de variável ou um número. Se eu substituir acom 1na Roundfunção, ele usa o valor 1. Se eu tentar %1%, ele tenta usar o conteúdo do primeiro argumento como um nome de variável, o que não funciona. Ter que defini-lo como outra variável primeiro me custou 6 bytes.


3

Lote, 390 bytes

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

Explicação. Começa extraindo o sinal, se aplicável. Em seguida, divide o número em dígitos inteiros e de fração. A fração é preenchida com n+1zeros para garantir que tenha mais do que ndígitos. O nth dígito (indexado a zero) é dividido por 5, e esse é o transporte inicial. Os ndígitos do número inteiro e da fração são concatenados e o carry é adicionado caractere por caractere. (Os zeros extras se protegem contra a ondulação de transporte.) Após o transporte parar de ondular, o número é reconstruído e qualquer ponto decimal inserido.


3

TI-Basic, 53 16 bytes

O TI-Basic não usa IEEE e o método abaixo funciona para 0-9 (inclusive) casas decimais.

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

Obrigado a @JulianLachniet por mostrar que os cálculos CE têm o toString( comando que eu não conhecia (são necessários o Color Edition calcs OS 5.2 ou superior).

PS: Eu tinha uma segunda linha, sub(Str1,1,N+inString(Str1,".mas depois percebi que era inútil.


Como é Nusado?
Matthias

@ Matthias Obrigado por pegar esse erro de digitação! Eu acidentalmente removido nos últimos três bytes com a minha edição anterior
Timtech

3

Java 7, 77 72 71 bytes

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

-1 byte graças a @cliffroot

Resposta de 72 bytes:

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

Ao contrário do Python, o Java já arredonda corretamente e já retorna uma String quando você usa String.format("%.2f", aDouble)o valor 2substituído com a quantidade de decimais desejada.

EDIT / NOTE: Sim, eu sei que new Float(n)é 1 byte menor que new Double(n), mas aparentemente falha nos casos de teste com 123456789.987654321. Veja este código de teste sobre Double vs Float.

Explicação:

<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

Código do teste:

Experimente aqui.

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

Resultado:

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

11
Um byte mais curto: <T>T c(T n,int d){return(T)"".format("%."+d+"f",new Double(n+""));}
cliffroot 17/03/17

2
Esta solução não funciona . Embora o exemplo seja potencialmente um problema meio-par / 0-fora de casa, ocorrem erros de ponto flutuante e o OP esclareceu que a precisão arbitrária deve ser suportada.
CAD97

11
De fato, você falha nos casos de exemplo na pergunta que você reproduziu aqui: 123456789.987654321, 4deveria ser 123456789.9877, não123456789.9876
CAD97

2

Python (2/3), 394 bytes

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

Trabalha para números de precisão arbitrários.


5
Ei, e bem-vindo ao PPCG! No entanto, isso não é jogado. Há muito espaço em branco que você pode remover. É necessário que as respostas neste site sejam jogadas de golfe, desculpe.
Rɪᴋᴇʀ

Apenas algumas coisas (provavelmente há muito mais) ... O nome da função pode ser de um byte. A primeira linha pode usar s[0]<'0'e também pode usar a multiplicação de cadeias m='-'*(s[0]<'0'),. Linhas sem nenhum intervalo de declaração de bloco podem ser unidas com ;(por exemplo o='';c=0). ifProvavelmente, algumas instruções podem ser substituídas pela indexação de lista para reduzir ainda mais a necessidade de quebras de linha e guias. A linha final pode usar uma fatia o[::-1], em vez de reversed(o)e ''.joiné redundante. Você também pode reescrevê-lo para evitar a necessidade de várias returninstruções.
Jonathan Allan

2
... se você estiver interessado, há dicas para jogar golfe em Python aqui .
Jonathan Allan

2

JavaScript (ES6), 155 bytes

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

Explicação: A sequência é normalizada primeiro para conter os dígitos a .e n+1decimal. O dígito à direita, qualquer 9s ou .s anterior e qualquer dígito anterior, são considerados. Se o último dígito for menor que 5, ele e qualquer um imediatamente anterior .serão simplesmente removidos, mas se for maior que 5, os 9s serão alterados para se 0o dígito anterior será incrementado (ou 1 prefixado se não houver dígito anterior).



1

Scala, 44 bytes

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

Teste:

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

Maravilha , 10 bytes

@@fix#1E#0

Uso:

@@fix#1E#0

Defina a precisão decimal e adicione zeros à direita, se necessário.


Existe um TIO para este?
Matthias

Não, não existe, mas a instalação é bem fácil. Verifique se você possui o Node.js (v6 +) e npm i -g wonderlang. Use o wondercomando para o fogo até a REPL e colar o código no.
Mama Fun rolo

1

J, 22 17 bytes

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

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

Agradeço a @Conor O'Brien por corrigir minha compreensão das regras.

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.

O desafio exige que você pegue o número de dígitos após o ponto decimal para arredondar para N decimais, não para N pontos de precisão. Como tal, 2 t '1234.456'deve dar em 1234.46vez de6 t '1234.456'
Conor O'Brien
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.