Autour de la chaîne


10

Certains nombres décimaux ne peuvent pas être représentés avec précision comme des flottants binaires en raison de la représentation interne des flotteurs binaires. Par exemple: arrondir 14,225 à deux chiffres décimaux ne donne pas 14,23 comme on pouvait s'y attendre mais 14,22.

Python :

In: round(14.225, 2)
Out: 14.22

Supposons cependant que nous ayons une représentation sous forme de chaîne de 14,225 en tant que «14,225», nous devrions pouvoir atteindre l'arrondi souhaité «14,23» en tant que représentation sous forme de chaîne.

Cette approche peut être généralisée à une précision arbitraire.

Solution Python 2/3 possible

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

Essayez-le en ligne!

Utilisation :

     # 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

Tâche

Argument d'entrée 1 : une chaîne contenant

  • au moins un chiffre ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
  • au plus un point décimal ( .) qui doit être précédé d'au moins un chiffre,
  • un moins optionnel ( -) comme premier caractère.

Argument d'entrée 2 : un entier non négatif

Sortie : la chaîne correctement arrondie (base 10)

arrondi = Arrondir la moitié de zéro

Ceci est un . Le plus petit nombre d'octets gagne!


@KevinCruijssen 1) Vous n'avez pas besoin de vous en tenir aux chaînes dans le corps de votre implémentation et vous êtes autorisé à utiliser l'arrondi intégré. Malheureusement (pour la question) la norme IEEE 754 est une norme largement utilisée et donc l'arrondi intégré ne donnera pas le comportement souhaité. 2) Ok, je n'étais pas au courant du bac à sable.
Matthias

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

1
Concernant le deuxième argument d'entrée: 0n'est pas un entier positif, il est "non négatif".
Stewie Griffin

1
Je suppose que nous ajoutons des zéros de fin si nécessaire? Pourriez-vous peut-être ajouter un cas de test pour 123.4 & 5 --> 123.40000? Ou peut-on supposer que la deuxième entrée ne sera jamais supérieure à la quantité de décimales après le point de la première entrée?
Kevin Cruijssen

1
@Matthias Sauf si vous pouvez intégrer le Python avec le JavaScript (je n'ai jamais programmé Python, et à peine JS, donc je ne sais vraiment pas si c'est possible) non. Mais vous pouvez toujours ajouter un lien Essayer en ligne avec votre code de test. EDIT: En outre, il est généralement préférable d'attendre au moins quelques jours avant d'accepter une réponse.
Kevin Cruijssen

Réponses:



5

Perl, 22 20 octets

printf"%.*f",pop,pop

En utilisant:

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

Il s'agit de la version de code de Dada. Précédent:

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

2
printf"%.*f",pop,popdevrait fonctionner
Dada

5

PHP, 33 31 octets

PHP arrondit également correctement (au moins sur 64 bits):

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

prend l'entrée des arguments de ligne de commande. Courez avec -r.

PHP, pas intégré, 133 octets

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

Exécutez-le -nrou testez-le en ligne .

panne

[,$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 octet nul ne fonctionne pas; donc je dois utiliser substr.


1
Vous pouvez écrire "%.$argv[2]f"au lieu de "%.{$argv[2]}f", en économisant 2 octets.
Ismael Miguel

4

Rubis 2,3, 12 + 45 = 57

Utilise le BigDecimalintégré, mais il doit être requis avant utilisation, ce qui est moins cher à faire comme indicateur.

le drapeau: -rbigdecimal

la fonction:

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

Ruby 2.3 utilise par défaut ROUND_HALF_UP


4

Javascript (ES6), 44 octets

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

Essayez-le en ligne:

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 96 91 89 octets

5 octets enregistrés grâce à Kevin Cruijssen
2 octets enregistrés grâce à Krazor

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

Essayez-le en ligne!


1
from decimal import *et la suppression des trois d.est de 4 octets plus courte.
Kevin Cruijssen

@KevinCruijssen: Merci!
Emigna

2
Vous pourriez aussi faire d=Decimalet d() , ce qui économiserait encore 5. (Peut-être tort, très endormi)
FMaz

@Krazor: à moins que je ne me trompe, cela m'a fait économiser 2 octets. Merci!
Emigna

Woops, c'est ce que je voulais dire. Laissera mes pensées endormies de toute façon.
FMaz


3

BASH, 26 23 21 octets

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

usage

enregistrer dans round_string.sh, chmod + x round_string.sh

./round_string.sh 23456789.987654321 3

edit: pas besoin de charger la bibliothèque


Explication: bc utilise une précision arbitraire, créez un document ici avec '<<<' contenant la valeur de l'échelle comme deuxième paramètre et le premier paramètre divisé par 1 pour forcer l'interprétation de l'échelle.
marcosm

2
Cela donne 14.22pour l'entrée 14.225 2, et non14.23
Digital Trauma

3

AHK, 25 octets

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

Encore une fois, je suis déjoué par l'incapacité d'AHK à utiliser les paramètres passés directement dans les fonctions qui acceptent un nom de variable ou un nombre. Si je remplace apar 1dans la Roundfonction, il utilise la valeur 1. Si j'essaie %1%, il essaie d'utiliser le contenu du premier argument comme nom de variable, ce qui ne fonctionne pas. Devoir le définir comme une autre variable m'a d'abord coûté 6 octets.


3

Lot, 390 octets

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

Explication. Commence par extraire le signe, le cas échéant. Ensuite, divise le nombre en chiffres entiers et fractionnaires. La fraction est complétée par des n+1zéros pour garantir qu'elle comporte plus de nchiffres. Le ne chiffre (indexé zéro) est divisé par 5, et il s'agit du report initial. Les nchiffres entiers et fractionnaires sont concaténés, et le portage ajouté caractère par caractère. (Les zéros supplémentaires protègent contre l'ondulation du report.) Après l'arrêt du report du report, le nombre est reconstruit et tout point décimal inséré.


3

TI-Basic, 53 16 octets

TI-Basic n'utilise pas IEEE et la méthode ci-dessous fonctionne pour les positions décimales 0-9 (inclus).

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

Merci à @JulianLachniet d'avoir montré que les calculs CE ont la toString(commande que je ne connaissais pas (les calculs Color Edition OS 5.2 ou supérieur sont requis).

PS J'avais une deuxième ligne sub(Str1,1,N+inString(Str1,".mais j'ai réalisé que c'était inutile.


Comment est-il Nutilisé?
Matthias

@Matthias Merci d'avoir attrapé cette faute de frappe! J'ai accidentellement supprimé les trois derniers octets avec ma modification précédente
Timtech

3

Java 7, 77 72 71 octets

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

-1 octet grâce à @cliffroot

Réponse de 72 octets:

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

Contrairement à Python, Java arrondit déjà correctement et renvoie déjà une chaîne lorsque vous utilisez String.format("%.2f", aDouble) avec le 2remplacé par le nombre de décimales souhaité.

EDIT / NOTE: Oui, je sais que new Float(n)1 octet est plus court que new Double(n), mais apparemment, il échoue pour les cas de test avec123456789.987654321 . Voir ce code de test concernant Double vs Float.

Explication:

<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

Code de test:

Essayez-le ici.

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

Production:

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

2
Cette solution ne fonctionne pas . Bien que l'exemple soit potentiellement un problème rond demi-pair / absent-0, des erreurs en virgule flottante se produisent et OP a depuis précisé que la précision arbitraire devrait être prise en charge.
CAD97

1
En fait, vous échouez aux exemples de cas dans la question que vous avez reproduite ici: 123456789.987654321, 4devrait être 123456789.9877, pas123456789.9876
CAD97

2

Python (2/3), 394 octets

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

Fonctionne pour des nombres de précision arbitraires.


5
Hé, et bienvenue chez PPCG! Cependant, ce n'est pas joué au golf. Il y a beaucoup d'espace blanc que vous pouvez supprimer. Les réponses sur ce site doivent être jouées au golf, désolé.
Rɪᴋᴇʀ

Juste quelques choses (il y en a probablement beaucoup plus) ... Le nom de la fonction peut être un octet. La première ligne peut utiliser s[0]<'0'et pourrait également utiliser la multiplication des cordes, m='-'*(s[0]<'0'). Les lignes sans étendue de déclaration de bloc peuvent être jointes avec ;(par exemple o='';c=0). Certaines ifinstructions pourraient probablement être remplacées par une indexation de liste afin de réduire davantage le besoin de sauts de ligne et de tabulations. La ligne finale pourrait utiliser une tranche,, o[::-1]au lieu de reversed(o)et ''.joinest redondante. Vous pouvez également être en mesure de le réécrire pour éviter d'avoir besoin de plusieurs returninstructions.
Jonathan Allan

2
... si vous êtes intéressé, il y a conseils pour jouer au golf en Python .
Jonathan Allan

2

JavaScript (ES6), 155 octets

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

Explication: la chaîne est d'abord normalisée pour contenir un . etn+1 chiffre décimal. Le chiffre de fin, tout 9s ou .s précédent et tout chiffre précédent, sont alors considérés. Si le dernier chiffre est inférieur à 5, alors lui et tout précédent immédiatement .sont simplement supprimés mais s'il est supérieur à 5, alors le 9s est changé en 0s et le chiffre précédent incrémenté (ou 1 préfixé s'il n'y avait pas de chiffre précédent).



1

Scala, 44 octets

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

Tester:

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

Wonder , 10 octets

@@fix#1E#0

Usage:

@@fix#1E#0

Définissez la précision décimale et ajoutez des zéros de fin si nécessaire.


Y a-t-il un TIO pour celui-ci?
Matthias

Non, il n'y en a pas, mais l'installation est assez facile. Assurez-vous d'avoir Node.js (v6 +), et npm i -g wonderlang. Utilisez la wondercommande pour lancer le REPL et collez le code.
Mama Fun Roll

1

J, 22 17 octets

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

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

Merci à @Conor O'Brien d'avoir corrigé ma compréhension des règles.

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.

Le défi vous oblige à prendre le nombre de chiffres après la virgule décimale pour arrondir à N décimales, pas N points de précision. En tant que tel, 2 t '1234.456'devrait donner 1234.46au lieu de6 t '1234.456'
Conor O'Brien
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.