Вокруг строки


10

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

Python :

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 байтов
Джулиан Лакнет

1
Что касается второго входного аргумента: 0это не положительное целое число, это «неотрицательное».
Стьюи Гриффин

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

1
@Matthias Если вы не можете интегрировать Python с JavaScript (я никогда не программировал Python и только JS, поэтому я, честно говоря, не знаю, возможно ли это) нет. Но вы всегда можете добавить онлайн- ссылку Try it со своим тестовым кодом. РЕДАКТИРОВАТЬ: Кроме того, обычно лучше подождать хотя бы пару дней, пока вы не примете ответ.
Кевин Круйссен

Ответы:


2

APL (Дьялог) , 4 байта

Dyalog APL использует достаточную внутреннюю точность.

⎕⍕⍎⍞

Попробуйте онлайн!

⍎⍞ выполнить ввод строки

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


Почему это 4 вместо 8 байтов?
Матиас

@Matthias, потому что у Dyalog APL есть
SBCS

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

Python, 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: Спасибо!
Emigna

2
Вы также можете сделать d=Decimalи d() , что бы сохранить еще 5 (может быть неправильно, очень сонный)
FMaz

@Krazor: Если я не сделал это неправильно, это спасло мне 2 байта. Спасибо!
Emigna

Woops, это то, что я имел в виду. Все равно оставлю мои сонные мысли.
FMaz

4

REXX, 24 байта

arg n p
say format(n,,p)

Поскольку REXX всегда использует текстовое представление чисел, правильное округление чисел бесплатно.

Попробуйте онлайн!


Ух ты. хорошее злоупотребление языком.
Мэтью Ро

Есть ли в REXX онлайн-компилятор?
Кевин Круйссен

Это (в основном) интерпретируемый язык.
idrougge


3

BASH, 26 23 21 байт

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

Применение

сохранить в round_string.sh, chmod + x round_string.sh

./round_string.sh 23456789.987654321 3

редактировать: нет необходимости загружать библиотеку


Объяснение: bc использует произвольную точность, создайте здесь документ с «<<<», который содержит значение масштаба в качестве второго параметра и первый параметр, разделенный на 1, для принудительной интерпретации масштаба.
marcosm

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 за показ, что у CE calcs есть toString(команда, о которой я не знал (требуются Color Edition calcs OS 5.2 или выше).

PS У меня была вторая строка, sub(Str1,1,N+inString(Str1,".но потом я понял, что это бесполезно.


Как Nиспользуется?
Матиас

@Matthias Спасибо, что поймали эту опечатку! Я случайно удалил последние три байта с моим предыдущим редактированием
Timtech

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 против 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+""));}
Cliffroot

2
Это решение не работает . Хотя этот пример потенциально является проблемой круглых половинных / четных-0, ошибки с плавающей запятой действительно возникают, и с тех пор OP пояснил, что должна поддерживаться произвольная точность.
CAD97

1
Фактически, вы проваливаете примеры случаев в вопросе, который вы воспроизвели здесь: 123456789.987654321, 4должно быть 123456789.9877, а не123456789.9876
CAD97

2

Python (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, то9 s изменяются на 0s, а предыдущая цифра увеличивается (или с префиксом 1, если предыдущей цифры не было).



1

Скала, 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

Установите десятичную точность и добавьте конечные нули, если необходимо.


Есть ли TIO для этого?
Матиас

Нет, нет, но установка довольно проста. Убедитесь, что у вас есть 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.