Закруглюємо струну


10

Деякі десяткові числа не можуть бути точно представлені як двійкові поплавці через внутрішнє подання двійкових поплавків. Наприклад: округлення 14,225 до двох десяткових цифр не призводить до 14,23, як можна було очікувати, а до 14,22.

Пітон :

In: round(14.225, 2)
Out: 14.22

Припустимо, що у нас є рядкове представлення 14,225 як "14 .225", ми повинні мати можливість досягти бажаного округлення '14,23 'у вигляді рядкового подання.

Цей підхід можна узагальнити до довільної точності.

Можливе рішення 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

Спробуйте в Інтернеті!

Використання :

     # 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

Завдання

Вхідний аргумент 1 : рядок, що містить

  • щонайменше , одна цифра ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
  • щонайбільше однієї десяткової крапки ( .), якій має передувати принаймні одна цифра,
  • необов'язковий мінус ( -) як перший символ.

Вхідний аргумент 2 : невід'ємне ціле число

Вихід : правильно закруглена (основа 10) рядок

округлення = Кругла половина від нуля

Це . Виграє найменша кількість байтів!


@KevinCruijssen 1) Вам не потрібно дотримуватися рядків у тілі вашої реалізації та дозволено використовувати вбудоване округлення. На жаль (для питання) стандарт IEEE 754 є широко використовуваним стандартом, тому вбудоване округлення не призведе до бажаної поведінки. 2) Гаразд, не знав про пісочницю.
Маттіас

TI-Basic: round(A,B5 байт
Julian Lachniet

1
Щодо другого вхідного аргументу: 0не додатне ціле число, воно "невід'ємне".
Стюі Гріффін

1
Я припускаю, що ми додамо додаткові нулі, якщо потрібно? Не могли б ви додати тестовий випадок для 123.4 & 5 --> 123.40000? Чи можна припустити, що другий вхід ніколи не буде більшим за кількість десяткових знаків після точки на першому вході?
Кевін Кройсейсен

1
@Matthias Якщо ви не можете інтегрувати Python із JavaScript (я ніколи не програмував Python та ледве JS, тому я, чесно кажучи, не знаю, чи можливо) ні. Але ви завжди можете додати посилання " Спробуйте" онлайн із тестовим кодом. EDIT: Крім того, зазвичай краще почекати хоча б пару днів, поки ви не приймете відповідь.
Kevin Cruijssen

Відповіді:


2

APL (Dyalog) , 4 байти

Dyalog APL використовує достатню внутрішню точність.

⎕⍕⍎⍞

Спробуйте в Інтернеті!

⍎⍞ виконання рядкового введення

⎕⍕ отримати числовий ввід і використовувати це як точність для форматування


Чому це 4 замість 8 байт?
Маттіас

@Matthias Тому що Dyalog APL є SBCS
Adám

5

Perl, 22 20 байт

printf"%.*f",pop,pop

Використання:

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

Це версія коду Дади. Попередній:

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

2
printf"%.*f",pop,popповинен працювати
Дада

5

PHP, 33 31 байт

PHP також правильно округляється (принаймні, на 64 біт):

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

приймає дані з аргументів командного рядка. Бігайте з -r.

PHP, без вбудованих файлів, 133 байт

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

Виконати з -nrабо випробуйте його в Інтернеті .

зламатися

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

Нульовий байт не працює; тому мені доведеться користуватися substr.


1
Ви можете писати "%.$argv[2]f"замість "%.{$argv[2]}f", зберігаючи 2 байти.
Ісмаїл Мігель

4

Рубін 2,3, 12 + 45 = 57

Використовує BigDecimal вбудований, але це потрібно вимагати перед використанням, що дешевше зробити як прапор.

прапор: -rbigdecimal

функція:

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

За замовчуванням використовуйте Ruby 2.3 ROUND_HALF_UP


4

Javascript (ES6), 44 байти

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

Спробуйте в Інтернеті:

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

Пітон, 114 105 103 96 91 89 байт

Збережено 5 байт завдяки Кевіну Крейссену
Збережено 2 байти завдяки Кразору

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

Спробуйте в Інтернеті!


1
from decimal import *а вилучення трьох d.на 4 байти коротше.
Кевін Круїссен

@KevinCruijssen: Дякую!
Емінья

2
Крім того, можна зробити d=Decimalі d() , що б врятувати іншого 5. (Може бути неправильно, дуже сонним)
FMaz

@Krazor: Якщо я не помилився, врятував мене 2 байти. Дякую!
Емінья

Вуп, це я мав на увазі. В будь-якому разі залишать мої сонні думки.
FMaz

4

REXX, 24 байти

arg n p
say format(n,,p)

Оскільки REXX завжди використовує текстове подання чисел, правильне округлення чисел є вільним.

Спробуйте в Інтернеті!


Ого. приємне зловживання мовою.
Метью Рох

Чи REXX має онлайн-компілятор?
Kevin Cruijssen

Це (в основному) інтерпретована мова.
idrougge


3

БАШ, 26 23 21 байт

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

використання

зберегти в round_string.sh, chmod + x round_string.sh

./round_string.sh 23456789.987654321 3

редагувати: не потрібно завантажувати бібліотеку


Пояснення: bc використовує довільну точність, створіть тут doc з '<<<', що стосується значення масштабу як другого параметра, а перший параметр розділений на 1 для примусової інтерпретації шкали.
маркосм

2
Це дає 14.22вклад 14.225 2, а не14.23
Digital Trauma

3

AHK, 25 байт

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

Знову мене зриває неможливість AHK використовувати передані параметри безпосередньо у функціях, які приймають або ім’я змінної, або число. Якщо я заміню aз 1в Roundфункції, вона використовує значення 1. Якщо я спробую %1%, він намагається використовувати вміст першого аргументу як ім'я змінної, що не працює. Потрібно встановити його як іншу змінну спочатку коштувало мені 6 байт.


3

Пакетна, 390 байт

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

Пояснення. Починається з витягування знака, якщо це застосовується. Потім розбиває число на цілі і дробові цифри. Фракція засипана n+1нулями, щоб переконатися, що в ній більше nцифр. nЙ (нульовий індексований) цифра ділиться на 5, і це початковий перенесення. Цілі і nдробові цифри з'єднані, а перенос додається символом на знак. (Додаткові нулі захищають від пульсації носіння.) Після того, як перенесення перестає пульсувати, число реконструюється та вставляється будь-яка десяткова крапка.


3

TI-Basic, 53 16 байт

TI-Basic не використовує IEEE, і наведений нижче метод працює для 0-9 (включно) десяткових позицій.

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

Завдяки @JulianLachniet за те, що він показав, що у calcs CE є така toString(команда, про яку я не знав (потрібні кольорові версії calcs OS 5.2 або новіші).

PS У мене був другий рядок, sub(Str1,1,N+inString(Str1,".але потім я зрозумів, що це марно.


Як Nзастосовується?
Маттіас

@Matthias Дякую за те, що потрапив у цю помилку! Я випадково видалив останні три байти зі своєю попередньою
редакцією

3

Java 7, 77 72 71 байт

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

-1 байт завдяки @cliffroot

72-байтова відповідь:

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

На відміну від Python, Java вже правильно закріплює і вже повертає String, коли ви використовуєте String.format("%.2f", aDouble)із 2заміненою кількістю потрібних десяткових знаків.

РЕДАКТУВАННЯ / ПРИМІТКА: Так, я знаю new Float(n), що на 1 байт коротше new Double(n), але, мабуть, це не вдається для тестових випадків 123456789.987654321. Дивіться цей тестовий код щодо Double vs Float.

Пояснення:

<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

Код тесту:

Спробуйте тут.

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

Вихід:

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
На один байт коротше:<T>T c(T n,int d){return(T)"".format("%."+d+"f",new Double(n+""));}
скеля

2
Це рішення не працює . Незважаючи на те, що приклад потенційно є круглим питанням "напіврівняння / відсутнє" 0, помилки з плаваючою комою трапляються і з тих пір ОП уточнила, що довільну точність слід підтримувати.
CAD97

1
Насправді ви не 123456789.987654321, 4123456789.9877123456789.9876
вживаєте

2

Пітон (2/3), 394 байти

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

Працює для довільних точних чисел.


5
Гей, і ласкаво просимо до PPCG! Однак це не гольф. Існує багато пробілів, які ви можете видалити. Відповіді на цьому веб-сайті потрібні для гри в гольф, вибачте.
Rɪᴋᴇʀ

Просто деякі речі (можливо, набагато більше) ... Назва функції може бути одним байтом. Перша лінію можна використовувати s[0]<'0'і може також використовувати рядок множення, m='-'*(s[0]<'0'). Рядки без будь-яких проміжків оператора блоку можна з'єднати разом ;(наприклад o='';c=0). Деякі ifвисловлювання, ймовірно, можуть бути замінені індексуванням списку для подальшого зменшення потреби в розривах рядків та вкладках. В останньому рядку можна використовувати шматочок, o[::-1]замість того , reversed(o)і ''.joinє зайвим. Ви також можете переписати його, щоб уникнути необхідності в декількох returnвисловлюваннях.
Джонатан Аллан

2
... якщо вас цікавить, тут є поради щодо гри в гольф на Python .
Джонатан Аллан

2

JavaScript (ES6), 155 байт

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

Пояснення: рядок спочатку нормалізується, щоб містити знаки з десятковими .і n+1десятковими цифрами. Потім враховується кінцева цифра, будь-яка попередня 9s або .s, і будь-яка попередня цифра. Якщо остання цифра менше 5, то її та будь-яку, що безпосередньо передує ., просто видаляють, але якщо вона перевищує 5, то 9s змінюється на 0s та збільшується попередня цифра (або 1 префікс, якщо попередньої цифри не було).



1

Scala, 44 байти

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

Тест:

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

Диво , 10 байт

@@fix#1E#0

Використання:

@@fix#1E#0

Встановіть десяткову точність і, якщо потрібно, додайте проміжні нулі.


Чи є ТІО для цього?
Маттіас

Ні там немає, але установка досить проста. Переконайтеся, що у вас є Node.js (v6 +) та npm i -g wonderlang. Використовуйте wonderкоманду для запуску REPL та вставте код.
Mama Fun Roll

1

J, 22 17 байт

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

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

Дякую @Conor O'Brien за те, що виправив моє розуміння правил.

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.

Завдання вимагає, щоб ви взяли кількість цифр після десяткової крапки, щоб округлити до N десяткових знаків, а не N точок точності. Як таке, 2 t '1234.456'слід давати1234.46 замість6 t '1234.456'
Конор О'Брайен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.