用有效数字评估表达式


10

给定一个表达式,您的任务是评估它。但是,您的答案显示的位数不能超过必要的位数,因为这给人的印象是比实际情况更精确。

一个数字具有的有效位数是用科学计数法表示时具有的位数,如果存在小数点,则在末尾包含零。例如,1200有2个有效数字,因为它是,1.2*10^31200.有4个有效数字和1200.05个有效数字。

当将两个数字相加时,结果应四舍五入到与最低有效数字最左边的数字相同的位数。例如,1200 + 3 = 1200(自1200以来四舍五入到数百位)1200.01 + 3 = 1203,和4.59 + 2.3 = 6.9。请注意5四舍五入。该规则也适用于减法。0四舍五入到那个地方。请注意,加法和减法不取决于有效位数。例如,999 + 2.00 = 1001因为999四舍五入到一个位,而2.00四舍五入到百分之一;一个舍入到较少位数的位数是999,因此结果1001.00也应舍入到那个位数。同样,300 + 1-300精确等于1,但是300会四舍五入到百位,因此最终结果也应该四舍五入到百位,得出0。300. + 1-300.将等于1。另一方面。

当将两个数字相乘或相除时,请四舍五入到数字的有效位数,即最低有效位数。例如,3.839*4=20由于精确值,15.356四舍五入到20以来4,只有一个有效数字。同样,100/4=30由于两个数字都有一个有效数字,但是100./4.00=25.0由于两个数字都有3个有效数字。0被定义为具有1个有效数字。

表达式将只包含*/+,和-,(和括号)。应当遵循操作顺序,并且每次操作后都应将结果四舍五入。如果在一系列加法或减法或一串乘法与除法中遗漏了括号,则在完成所有运算后四舍五入。例如,6*0.4*2 = 5(一个有效数字),而0.4*(2*6)=0.4*10=4(6*0.4)*2=2*2=4

输入:一个字符串,其表达式包含()*/+-和数字。为简化起见,-将仅用作减法运算符,而不表示负数;但是,答案可能仍然是否定的,并且需要-作为前缀。

输出:表达式的结果,求值并四舍五入为正确的位数。请注意,这25对于不正确25.0

测试用例

3 + 0.5 --> 4
25.01 - 0.01 --> 25.00
4*7*3 --> 80
(4*7)*3 --> 90
(8.0 + 0.5)/(2.36 - 0.8 - 0.02) --> 5.7
6.0 + 4.0 --> 10.0
5.0 * 2.0 --> 10.0
1/(2.0 * (3.0 + 5.0)) --> 0.06
0.0020 * 129 --> 0.26
300 + 1 - 300 --> 0
0 - 8.8 --> -9
3*5/2*2 --> 20

边缘情况:考虑问题501*2.0。确切值为1002。打印1002给出的有效数字太多(需要时为2,则为4),但1000给出的有效数字太多(当需要时为2,则为1)。在这种情况下,您的程序1000仍然应该打印。

该来源也解释了有效数字:http : //www.purplemath.com/modules/rounding2.htm


相同数量的地方 ” 是什么意思?这与“ 相同数量的有效数字相同吗?如果要添加边缘盒,请使用999 + 2.00
彼得·泰勒

当然300 + 1 - 300是一串加法和减法,因此不需要四舍五入到最后。(300 + 1) - 300将为零。
尼尔

Answers:


9

Java 11,1325 1379 1356 1336 1290字节

import java.math.*;String c(String s)throws Exception{String r="",T=r,a[],b[],z="\\.";int i=0,l,A[],M=0,m=s.length(),j,f=0,q=m;if(s.contains("(")){for(;i<m;){var c=s.charAt(i++);if(f<1){if(c==40){f=1;continue;}r+=c;}else{if(c==41&T.replaceAll("[^(]","").length()==T.replaceAll("[^)]","").length()){r+="x"+s.substring(i);break;}T+=c;}}return c(r.replace("x",c(T)));}else{for(a=s.split("[\\+\\-\\*/]"),A=new int[l=a.length];i<l;f=b.length>1&&(j=b[1].length())>f?j:f)M=(j=(b=a[i++].split(z))[0].length())>M?j:M;for(b=a.clone(),i=0;i<l;A[i]=b[i].contains(".")?j=b[i].length()-1:b[i].replaceAll("0*$","").length(),i++)for(q=(j=b[i].replace(".","").length())<q?j:q,j=a[i].split(z)[0].length();j++<M;)b[i]=0+b[i];double R=new Double(new javax.script.ScriptEngineManager().getEngineByName("JS").eval(s)+""),p;for(int x:A)m=x<m?x:m;m=m==M&R%1==0&(int)R/10%10<1&(j=(r=R+"").split(z)[0].length())>m?j-q>1?q:j:R>99?m:R%10==0?r.length()-1:m<1?1:m;R=new BigDecimal(R).round(new MathContext((R<0?-R:R)<1?m-1:m)).doubleValue();r=(m<M&(p=Math.pow(10,M-m))/10>R?(int)(R/p)*p:R)+"";l=r.length()-2;r=(r=f<1?r.replaceAll(z+"0$",""):r+"0".repeat(f)).substring(0,(j=r.length())<m?j:r.contains(".")?(j=r.replaceAll("^0\\.0+","").length())<m?m-~j:m+1:m);for(i=r.length();i++<l;)r+=0;return r.replaceAll(z+"$","");}}

+54个字节来修复边缘情况501*2.01002之前给出了结果,但现在更正了1000)。

我现在明白为什么这个挑战将近两年没有得到解决。
>>> 这个挑战比荷兰语(这是在说些什么)具有更多的特殊情况。Java绝对不是解决这类挑战(或任何代码高尔夫)的正确语言挑战..; p),但这是我所知道的唯一一种语言,甚至可以尝试像这样的困难挑战。

输入格式为String无空格(如果不允许,则可以s=s.replace(" ","")在方法顶部添加(+19字节))。

在线尝试。

说明:

抱歉,很长的帖子。

if(s.contains("(")){
  for(;i<m;){
    var c=s.charAt(i++);
    if(f<1){
      if(c==40){
        f=1;
        continue;}
      r+=c;}
    else{
      if(c==41&T.replaceAll("[^(]","").length()==T.replaceAll("[^)]","").length()){
        r+="x"+s.substring(i);
        break;}
      T+=c;}}
  return c(r.replace("x",c(T)));}

此部分用于包含括号的输入。它将获得分离的部分并使用递归调用。

  • 0.4*(2*6)成为0.4*A,哪里A有递归调用c(2*6)
  • (8.3*0.02)+(1.*(9*4)+2.2)成为A+B,对A的递归调用c(8.3*0.02)和→ B的递归调用,而c(1.*(9*4)+2.2)→依次变为1.*C+2.2,对C的递归调用c(9*4)

for(a=s.split("[\\+\\-\\*/]"),A=new int[l=a.length];
    i<l;
    f=b.length>1&&(j=b[1].length())>f?j:f)
  M=(j=(b=a[i++].split(z))[0].length())>M?j:M;

第一个循环用于填充Mk,其中M是有效数字的最大整数长度和k最大十进制长度。

  • 1200+3.0变成M=2, k=112, .0
  • 999+2.00变成M=3, k=2999, .00
  • 300.+1-300.变成M=3, k=0300, .

for(b=a.clone(),i=0;
    i<l;
    A[i]=b[i].contains(".")?j=b[i].length()-1:b[i].replaceAll("0*$","").length(),i++)
  for(q=(j=b[i].replace(".","").length())<q?j:q,
      j=a[i].split(z)[0].length();
      j++<M;)
    b[i]=0+b[i];

第二个循环用于填充数组Abvalue q,其中A有效数字的数量,b保留前导零以匹配的整数M以及q不考虑点的最小长度。

  • 1200+3.0A=[2, 5] (12, 00030)b=[1200, 0003.0]q=230
  • 999+2.00成为A=[3, 5] (999, 00200)b=[999, 002.00]q=3((999200
  • 300.+1-300.A=[3, 3, 3] (300, 001, 300)b=[300., 001, 300.]q=11
  • 501*2.0A=[3, 4] (501, 0020)b=[501, 002.0]q=220

double R=new Double(new javax.script.ScriptEngineManager().getEngineByName("JS").eval(s)+"")

使用JavaScript引擎评估输入,该输入将保存R为double。

  • 1200+3.0 变成 R=1203.0
  • 999+2.00 变成 R=1001.0
  • 300.+1-300. 变成 R=1.0

for(int x:A)
  m=x<m?x:m;

这设置m为数组中的最小值A

  • A=[2, 5] 变成 m=2
  • A=[3, 5] 变成 m=3
  • A=[3, 3, 3] 变成 m=3

 m=m==M                // If `m` equals `M`
   &R%1==0             // and `R` has no decimal values (apart from 0)
   &(int)R/10%10<1     // and floor(int(R)/10) modulo-10 is 0
   &(j=(r=R+"").split(z)[0].length())>m?
                       // and the integer-length of R is larger than `m`:
    j-q>1?             //  If this integer-length of `R` minus `q` is 2 or larger:
     q                 //   Set `m` to `q` instead
    :                  //  Else:
     j                 //  Set `m` to this integer-length of `R`
   :R>99?              // Else-if `R` is 100 or larger:
    m                  //  Leave `m` the same
   :R%10==0?           // Else-if `R` modulo-10 is exactly 0:
    r.length()-1       //  Set `m` to the total length of `R` (minus the dot)
   :m<1?               // Else-if `m` is 0:
    1                  //  Set `m` to 1
   :                   // Else:
    m;                 //  Leave `m` the same

m基于多个因素进行修改。

  • 999+2.00 = 1001.0m=3,q=3成为m=4(因为m==M(都3)→ R%1==01001.0没有十进制值)→ (int)R/10%10<1(int)1001.0/10变成100100%10<1)→ "1001".length()>m4>3)→ "1001".length()-q<=14-3<=1)→从而m变成整数部分的长度"1001"4))
  • 3.839*4 = 15.356m=1,q=1保持m=1(因为m==M(都1)→ R%1!=015.356具有十进制值)R<=99→→ R%10!=015.356%10==5.356m!=0→→因此m保持不变(1))
  • 4*7*3 = 84.0m=1,q=1保持m=1(因为m==M(均为1)→ R%1==084.0没有十进制值)→ (int)R/10%10>=1(int)84/10变成88%10>=1R<=99→→ R%10!=084%10==4m!=0→→因此m保持不变(1))
  • 6.0+4.0 = 10.0m=2,q=2变为m=3(因为m!=Mm=2, M=1)→ R<=99R%10==010%10==0)→所以m成为总的长度R(减去点)"10.0".length()-13))
  • 0-8.8 = -8.8m=0,q=1变为m=1(因为m!=Mm=0, M=1)→交通R<=99→交通R%10!=0-8.8%10==-8.8)→交通m<1→交通所以m变得1
  • 501*2.0 = 1001.0m=3,q=2成为m=2(因为m==M(都3)→ R%1==01001.0没有十进制值)→ (int)R/10%10<1(int)1001.0/10变成100100%10<1)→ "1001".length()>m4>3)→ "1001".length()-q>14-2>1)→因此m变成q2))

R=new BigDecimal(R).round(new MathContext((R<0?-R:R)<1?m-1:m)).doubleValue();

现在R基于进行四舍五入m

  • 1001.0m=4成为1001.0
  • 0.258m=3变为0.26(因为abs(R)<1m-12),而不是m=3使用内部MathContext
  • -8.8m=1成为-9.0
  • 1002.0m=2成为1000.0

m<M&(p=Math.pow(10,M-m))/10>R?(int)(R/p)*p:R;

R如果需要,这将修改的整数部分。

  • 300.+1-300. = 1.0m=3,M=3停留1.0(因为m>=M→因此R保持不变(1.0))
  • 0.4*10 = 4.0m=1,M=24.0(因为m<M(10^(M-m))/10<=R(10^1)/10<=4.010/10<=4.01<=4.0)→所以R保持不变(4.0))
  • 300+1-300 = 1.0m=1,M=3变为0.0(因为m<M(10^(M-m))/10>R(10^2)/10>1.0100/10>1.010>1.0)→所以R成为0.0int(R/(10^(M-m)))*(10^(M-m))int(1.0/(10^2))*(10^2)int(1.0/100)*1000*1000

r=(...)+"";                  // Set `R` to `r` as String (... is the part explained above)
l=r.length()-2;              // Set `l` to the length of `R` minus 2
r=(r=k<1?                    // If `k` is 0 (no decimal values in any of the input-numbers)
      r.replaceAll(z+"0$","")
                             //  Remove the `.0` at the end
     :                       // Else:
      r+"0".repeat(f)
                             //  Append `k` zeroes after the current `r`
  ).substring(0,             // Then take the substring from index `0` to:
     (j=r.length())<m?       //  If the total length of `r` is below `m`:
       j                     //   Leave `r` the same
     :r.contains(".")?       //  Else-if `r` contains a dot
       (j=r.replaceAll("^0\\.0+","").length())<m?
                             //   And `R` is a decimal below 1,
                             //   and its rightmost decimal length is smaller than `m`
        m-~j                 //    Take the substring from index 0 to `m+j+1`
                             //    where `j` is this rightmost decimal length
       :                     //   Else:
        m+1                  //    Take the substring from index 0 to `m+1`
     :                       //  Else:
      m);                    //   Take the substring from index 0 to `m`

这台Rr字符串,并修改其基于多个因素。

  • 1203.0m=4,k=2变为1203.(因为k>=1r变为1001.000; r.length()>=m8>=4r.contains(".")→→ r.length()>=m8>=4)→从索引0m+15)的子字符串)
  • 6.9m=2,k=2停留6.9(因为k>=1r变成6.900; r.length()>=m5>=2r.contains(".")→→ r.length()>=m5>=2)→从索引0m+13)的子字符串)
  • 1.0m=3,k=0变成1(因为k<1r变成1; r.length()<m1<3)→从索引0r.length()1)的子字符串)
  • 25.0m=4,k=4变为25.00(因为k>=1r变为25.00000; r.length()>=m8>=4r.contains(".")→→ r.length()>+m8>=4)→从索引0m+15)的子字符串)
  • 0m=1,k=0停留0(因为k<1→所以r停留0; r.length()>=m1>=1)→→ !r.contains(".")从索引0m1)的子串)

for(i=r.length();i++<l;)
  r+=0;

如有必要,这会将尾随零再放回整数部分。

  • r="12"R=1200.0成为r="1200"
  • r="1"R=10.0成为r="10"
  • r="8"R=80.0成为r="80"

return r.replaceAll(z+"$","");

在删除了所有尾随点后,最后返回结果。

  • 1203. 变成 1203
  • 5. 变成 5

绝对可以打成两百个字节,但是我很高兴它现在可以工作了。了解每个案例以及挑战中提出的要求已经花费了一段时间。然后,经过反复试验,测试和重新测试才能得出上述结果。在上面编写此说明时,我能够删除另外±50字节的未使用代码。


1
已投票。但是规范似乎要求501*2.0输出10001000 无论如何,您都应该输出,我将其解释为“仍然”,而不是两种方式)。无论如何,宏伟的工作。

1
@WeijunZhou感谢您的反馈!我再三考虑了一下,并且能够在不破坏其他任何情况的情况下修复边缘情况。:)
Kevin Cruijssen
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.