Alrededor de la cuerda


10

Algunos números decimales no se pueden representar con precisión como flotantes binarios debido a la representación interna de los flotantes binarios. Por ejemplo: redondear 14.225 a dos dígitos decimales no da como resultado 14.23 como cabría esperar sino 14.22.

Python :

In: round(14.225, 2)
Out: 14.22

Sin embargo, supongamos que tenemos una representación de cadena de 14.225 como '14 .225 ', deberíamos poder lograr nuestro redondeo deseado '14 .23' como una representación de cadena.

Este enfoque puede generalizarse con precisión arbitraria.

Posible solución de 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

Pruébalo en línea!

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

Tarea

Argumento de entrada 1 : una cadena que contiene

  • al menos un dígito ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
  • como máximo un punto decimal ( .) que debe estar precedido por al menos un dígito,
  • un menos opcional ( -) como primer carácter.

Argumento de entrada 2 : un entero no negativo

Salida : la cadena correctamente redondeada (base 10)

redondeo = redondear la mitad lejos de cero

Este es un . ¡El número más bajo de bytes gana!


@KevinCruijssen 1) No necesita pegarse a las cadenas en el cuerpo de su implementación y puede usar el redondeo incorporado. Desafortunadamente (para la pregunta), el estándar IEEE 754 es un estándar ampliamente utilizado y, por lo tanto, el redondeo incorporado no dará como resultado el comportamiento deseado. 2) Ok, no estaba al tanto de la caja de arena.
Matthias

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

1
Con respecto al segundo argumento de entrada: 0no es un entero positivo, es "no negativo".
Stewie Griffin

1
¿Supongo que agregamos ceros finales si es necesario? ¿Podría quizás agregar un caso de prueba 123.4 & 5 --> 123.40000? ¿O podemos suponer que la segunda entrada nunca será mayor que la cantidad de decimales después del punto en la primera entrada?
Kevin Cruijssen

1
@Matthias A menos que pueda integrar Python con JavaScript (nunca he programado Python, y apenas JS, así que honestamente no sé si es posible) no. Pero siempre puede agregar un enlace Probar en línea con su código de prueba. EDITAR: Además, generalmente es mejor esperar al menos un par de días hasta que acepte una respuesta.
Kevin Cruijssen

Respuestas:



5

Perl, 22 20 bytes

printf"%.*f",pop,pop

Utilizando:

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

Es la versión de código de Dada. Anterior:

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

2
printf"%.*f",pop,popdebería funcionar
Dada

5

PHP 33 31 bytes

PHP también se redondea correctamente (al menos en 64 bits):

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

toma datos de los argumentos de la línea de comandos. Corre con-r .

PHP, sin integraciones, 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;

Ejecutar -nro probarlo en línea .

Descompostura

[,$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 nulo no funciona; así que tengo que usar substr.


1
Puede escribir en "%.$argv[2]f"lugar de "%.{$argv[2]}f"guardar 2 bytes.
Ismael Miguel

4

Rubí 2.3, 12 + 45 = 57

Utiliza el BigDecimalincorporado, pero necesita ser requerido antes de su uso, lo cual es más barato que hacer como una bandera.

la bandera: -rbigdecimal

la función:

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

Ruby 2.3 utiliza por defecto ROUND_HALF_UP


4

Javascript (ES6), 44 bytes

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

Pruébalo en línea:

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 gracias a Kevin Cruijssen
Guardado 2 bytes gracias a Krazor

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

Pruébalo en línea!


1
from decimal import *y eliminar los tres d.es 4 bytes más corto.
Kevin Cruijssen

@KevinCruijssen: ¡Gracias!
Emigna

2
También podría hacerlo d=Decimaly d() , lo que ahorraría otros 5. (Podría estar equivocado, con mucho sueño)
FMaz

@Krazor: a menos que lo haya hecho mal, me ahorró 2 bytes. ¡Gracias!
Emigna

Woops, eso es lo que quise decir. Dejaré mis pensamientos somnolientos de todos modos.
FMaz


3

BASH, 26 23 21 bytes

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

uso

guardar en round_string.sh, chmod + x round_string.sh

./round_string.sh 23456789.987654321 3

editar: no es necesario cargar la biblioteca


Explicación: bc utiliza una precisión arbitraria, cree un documento aquí con '<<<' que contenga el valor de la escala como el segundo parámetro y el primer parámetro dividido por 1 para forzar la interpretación de la escala.
marcosm

2
Esto da 14.22para la entrada 14.225 2, y no14.23
Digital Trauma

3

AHK, 25 bytes

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

Una vez más, estoy frustrado por la incapacidad de AHK para usar parámetros pasados ​​directamente en funciones que aceptan un nombre de variable o un número. Si reemplazo acon 1en la Roundfunción, usa el valor 1. Si lo intento %1%, intenta usar el contenido del primer argumento como un nombre de variable, que no funciona. Tener que configurarlo como otra variable primero me costó 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%%

Explicación. Comienza por extraer el signo, si corresponde. Luego, divide el número en dígitos enteros y fraccionarios. La fracción se rellena con n+1ceros para garantizar que tenga más de ndígitos. El ndígito th (indexado a cero) se divide por 5, y este es el carry inicial. Los ndígitos enteros y fraccionarios se concatenan, y el acarreo se agrega carácter por carácter. (Los ceros adicionales protegen contra la ondulación de transporte). Después de que el transporte deja de ondularse, el número se reconstruye y se inserta cualquier punto decimal.


3

TI-Basic, 53 16 bytes

TI-Basic no utiliza IEEE y el método siguiente funciona para posiciones decimales 0-9 (inclusive).

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

Gracias a @JulianLachniet por mostrar que los calcs CE tienen toString( comando que no conocía (se requieren calcs Color Edition OS 5.2 o superior).

PD: tuve una segunda línea, sub(Str1,1,N+inString(Str1,".pero luego me di cuenta de que era inútil.


¿Cómo se Nusa?
Matthias

@Matthias ¡Gracias por atrapar ese error tipográfico!
Eliminé

3

Java 7, 77 72 71 bytes

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

-1 byte gracias a @cliffroot

Respuesta de 72 bytes:

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

A diferencia de Python, Java ya se redondea correctamente y ya devuelve una cadena cuando la usa String.format("%.2f", aDouble)con2 reemplazado con la cantidad de decimales que desee.

EDITAR / NOTA: Sí, sé que new Float(n)es 1 byte más corto que new Double(n), pero aparentemente falla para los casos de prueba con 123456789.987654321. Vea este código de prueba con respecto a Double vs Float.

Explicación:

<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 de prueba:

Pruébalo aquí.

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

Salida:

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 más corto:<T>T c(T n,int d){return(T)"".format("%."+d+"f",new Double(n+""));}
cliffroot

2
Esta solución no funciona . Aunque el ejemplo es potencialmente un problema redondo de medio par / ausente 0, se producen errores de coma flotante y desde entonces OP ha aclarado que se debe admitir la precisión arbitraria.
CAD97

1
De hecho, usted falla los casos de ejemplo en la pregunta que reprodujo aquí: 123456789.987654321, 4debería ser 123456789.9877, no123456789.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))

Funciona para números de precisión arbitrarios.


55
¡Hola y bienvenidos a PPCG! Sin embargo, esto no es golf. Hay muchos espacios en blanco que puede eliminar. Las respuestas en este sitio son necesarias para jugar golf, lo siento.
Rɪᴋᴇʀ

Solo algunas cosas (es probable que haya mucho más) ... El nombre de la función puede ser un byte. La primera línea se puede utilizar s[0]<'0'y también podría usar la multiplicación de cuerdas, m='-'*(s[0]<'0'). Las líneas sin ningún intervalo de instrucción de bloque se pueden unir con ;(p o='';c=0. Ej .). Algunas ifdeclaraciones podrían ser reemplazadas por la indexación de listas para reducir aún más la necesidad de saltos de línea y pestañas. La línea final podría usar un segmento o[::-1], en lugar de reversed(o)y ''.joines redundante. Es posible que también pueda reescribirlo para evitar la necesidad de múltiples returndeclaraciones.
Jonathan Allan

2
... si está interesado, aquí encontrará consejos para jugar al golf en Python .
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)

Explicación: La serie se normaliza primero en contener una .y n+1dígitos decimales. Luego se considera el dígito final, cualquier 9s o .s anterior y cualquier dígito anterior. Si el último dígito es menor que 5, entonces y cualquier precedente inmediato .simplemente se eliminan, pero si es mayor que 5, los 9s se cambian a 0sy el dígito anterior se incrementa (o 1 con el prefijo si no había un dígito anterior).



1

Scala, 44 bytes

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

Prueba:

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

Maravilla , 10 bytes

@@fix#1E#0

Uso:

@@fix#1E#0

Establezca la precisión decimal y agregue ceros finales si es necesario.


¿Hay un TIO para este?
Matthias

No, no lo hay, pero la instalación es bastante fácil. Asegúrese de tener Node.js (v6 +) y npm i -g wonderlang. Use el wondercomando para activar el REPL y pegue el código.
Mama Fun Roll

1

J, 22 17 bytes

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

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

Gracias a @Conor O'Brien por corregir mi comprensión de las reglas.

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.

El desafío requiere que tome la cantidad de dígitos después del punto decimal para redondear a N decimales, no N puntos de precisión. Como tal, 2 t '1234.456'debería dar en 1234.46lugar de6 t '1234.456'
Conor O'Brien
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.