四舍五入


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:包含以下内容的字符串

  • 至少一个数位(0123456789),
  • 最多1个小数点(.),且必须至少带一个数字,
  • 可选的减号(-)作为第一个字符。

输入参数2:非负整数

输出:正确舍入(以10为底)的字符串

取整= 取整

这是一个。最低字节数获胜!


@KevinCruijssen 1)您不需要在实现主体中坚持字符串,并且可以使用内置舍入。不幸的是(对于这个问题)IEEE 754标准是一种广泛使用的标准,因此内置的舍入将不会导致所需的行为。2)好吧,没有意识到沙箱。
Matthias

TI-基本:round(A,B5个字节
朱利安·拉希尼特

1
关于第二个输入参数:0不是正整数,而是“非负”。
Stewie Griffin

1
我认为如果需要,我们添加尾随零吗?您能否为其添加一个测试用例123.4 & 5 --> 123.40000?还是我们可以假设第二个输入永远不会大于第一个输入点之后的小数位数?
凯文·克鲁伊森

1
@Matthias除非您可以将Python与JavaScript集成在一起(我从未编程过Python,而且几乎没有JS,所以老实说我不知道​​是否可行)否。但是,您始终可以在测试代​​码中添加“ 在线试用”链接。编辑:另外,通常最好至少等待几天,直到您接受答案为止。
凯文·克鲁伊森

Answers:




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个字节。
Ismael Miguel

4

Ruby 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个字节由于凯文Cruijssen
保存2由于字节Krazor

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=Decimald() ,这将节省另一个5.(可能是错误的,很困)
FMaz

@Krazor:除非我做错了,否则为我节省了2个字节。谢谢!
Emigna

糟糕,这就是我的意思。无论如何都会让我困倦的想法浮出水面。
FMaz


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使用任意精度,在此处创建一个带有'<<<'的doc,将scale的值作为第二个参数,并将第一个参数除以1以强制比例尺解释。
marcosm

2
这提供14.22了输入14.225 2,而不是14.23
数字创伤

3

AHK,25个字节

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

再次让我感到困扰的是,AHK无法在接受变量名或数字的函数中直接使用传入的参数。如果我在函数中替换a为,它将使用value 。如果尝试使用,它将尝试将第一个参数的内容用作变量名,这将不起作用。必须先将其设置为另一个变量,我要花6个字节。1Round1%1%


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 calc具有toString(我不知道的命令(需要Color Edition calcs OS 5.2或更高版本)。

PS我确实有第二行,sub(Str1,1,N+inString(Str1,".但后来我意识到这是没有用的。


如何N使用?
Matthias

@Matthias感谢您抓住那个错字!我不小心删除了上一个编辑的最后三个字节
Timtech,2017年

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.format("%.2f", aDouble)2替换为所需的小数位数时已经返回了String 。

编辑/注意:是的,我知道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舍入的问题,但确实会出现浮点错误,并且OP自此明确了应该支持任意精度。
CAD97

1
实际上,您在此处重现的问题中的示例案例失败:123456789.987654321, 4应该为123456789.9877123456789.9876

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')。没有任何block语句跨度的行可以与;(例如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)

说明:首先将字符串标准化为包含a .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

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'
Conor O'Brien
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.