Java 11, 1325 1379 1356 1336 1290 bytes
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 bytes to fix the edge case 501*2.0
(gave result 1002
before, but now correct 1000
).
I now understand why this challenge was unanswered for almost two years.. >.> This challenge has more special cases than the Dutch language, which is saying something..
Java is certainly not the right language for these kind of challenges (or any codegolf challenge for that matter.. ;p), but it's the only language I know good enough to even attempt a difficult challenge like this.
Input format as String
without spaces (if that is not allowed, you can add s=s.replace(" ","")
(+19 bytes) at the top of the method).
Try it online.
Explanation:
Sorry for the long post.
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)));}
This part is used for input containing parenthesis. It will get the separated parts and use recursive-calls.
0.4*(2*6)
becomes 0.4*A
, where A
is a recursive call to c(2*6)
(8.3*0.02)+(1.*(9*4)+2.2)
becomes A+B
, where A
is a recursive call to c(8.3*0.02)
and B
a recursive call to c(1.*(9*4)+2.2)
→ which in turn becomes 1.*C+2.2
, where C
is a recursive call to 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;
This first loop is used to fill the values M
and k
, where M
is the largest integer-length regarding significant figures and k
the largest decimals-length.
1200+3.0
becomes M=2, k=1
(12, .0
)
999+2.00
becomes M=3, k=2
(999, .00
)
300.+1-300.
becomes M=3, k=0
(300, .
)
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];
This second loop is used to fill the arrays A
and b
as well as value q
, where A
is the amount of significant figures, b
hold the integers with leading zeroes to match M
, and q
is the lowest length disregarding dots.
1200+3.0
becomes A=[2, 5] (12, 00030)
, b=[1200, 0003.0]
, and q=2
(30
)
999+2.00
becomes A=[3, 5] (999, 00200)
, b=[999, 002.00]
, and q=3
(both 999
and 200
)
300.+1-300.
becomes A=[3, 3, 3] (300, 001, 300)
, b=[300., 001, 300.]
, and q=1
(1
)
501*2.0
becomes A=[3, 4] (501, 0020)
, b=[501, 002.0]
, and q=2
(20
)
double R=new Double(new javax.script.ScriptEngineManager().getEngineByName("JS").eval(s)+"")
Uses a JavaScript engine to eval the input, which will be saved in R
as double.
1200+3.0
becomes R=1203.0
999+2.00
becomes R=1001.0
300.+1-300.
becomes R=1.0
for(int x:A)
m=x<m?x:m;
This sets m
to the smallest value in the array A
.
A=[2, 5]
becomes m=2
A=[3, 5]
becomes m=3
A=[3, 3, 3]
becomes 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
This modifies m
based on multiple factors.
999+2.00 = 1001.0
& m=3,q=3
becomes m=4
(because m==M
(both 3
) → R%1==0
(1001.0
has no decimal values) → (int)R/10%10<1
((int)1001.0/10
becomes 100
→ 100%10<1
) → "1001".length()>m
(4>3
) → "1001".length()-q<=1
(4-3<=1
) → so m
becomes the length of the integer-part "1001"
(4
))
3.839*4 = 15.356
& m=1,q=1
stays m=1
(because m==M
(both 1
) → R%1!=0
(15.356
has decimal values) → R<=99
→ R%10!=0
(15.356%10==5.356
) → m!=0
→ so m
stays the same (1
))
4*7*3 = 84.0
& m=1,q=1
stays m=1
(because m==M
(both 1
) → R%1==0
(84.0
has no decimal values) → (int)R/10%10>=1
((int)84/10
becomes 8
→ 8%10>=1
) → R<=99
→ R%10!=0
(84%10==4
) → m!=0
→ so m
stays the same (1
))
6.0+4.0 = 10.0
& m=2,q=2
becomes m=3
(because m!=M
(m=2, M=1
) → R<=99
→ R%10==0
(10%10==0
) → so m
becomes the length of the total R
(minus the dot) "10.0".length()-1
(3
))
0-8.8 = -8.8
& m=0,q=1
becomes m=1
(because m!=M
(m=0, M=1
) → R<=99
→ R%10!=0
(-8.8%10==-8.8
) → m<1
→ so m
becomes 1
)
501*2.0 = 1001.0
& m=3,q=2
becomes m=2
(because m==M
(both 3
) → R%1==0
(1001.0
has no decimal values) → (int)R/10%10<1
((int)1001.0/10
becomes 100
→ 100%10<1
) → "1001".length()>m
(4>3
) → "1001".length()-q>1
(4-2>1
) → so m
becomes q
(2
))
R=new BigDecimal(R).round(new MathContext((R<0?-R:R)<1?m-1:m)).doubleValue();
Now R
is rounded based on m
.
1001.0
& m=4
becomes 1001.0
0.258
& m=3
becomes 0.26
(because abs(R)<1
, m-1
(2
) instead of m=3
is used inside MathContext
)
-8.8
& m=1
becomes -9.0
1002.0
& m=2
becomes 1000.0
m<M&(p=Math.pow(10,M-m))/10>R?(int)(R/p)*p:R;
This modifies the integer part of R
if necessary.
300.+1-300. = 1.0
& m=3,M=3
stays 1.0
(because m>=M
→ so R
stays the same (1.0
))
0.4*10 = 4.0
& m=1,M=2
stays 4.0
(because m<M
→ (10^(M-m))/10<=R
((10^1)/10<=4.0
→ 10/10<=4.0
→ 1<=4.0
) → so R
stays the same (4.0
))
300+1-300 = 1.0
& m=1,M=3
becomes 0.0
(because m<M
→ (10^(M-m))/10>R
((10^2)/10>1.0
→ 100/10>1.0
→ 10>1.0
) → so R
becomes 0.0
because of int(R/(10^(M-m)))*(10^(M-m))
(int(1.0/(10^2))*(10^2)
→ int(1.0/100)*100
→ 0*100
→ 0
)
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`
This sets R
to r
as String, and modifies it based on multiple factors.
1203.0
& m=4,k=2
becomes 1203.
(because k>=1
→ so r
becomes 1001.000
; r.length()>=m
(8>=4
) → r.contains(".")
→ r.length()>=m
(8>=4
) → substring from index 0
to m+1
(5
))
6.9
& m=2,k=2
stays 6.9
(because k>=1
→ so r
becomes 6.900
; r.length()>=m
(5>=2
) → r.contains(".")
→ r.length()>=m
(5>=2
) → substring from index 0
to m+1
(3
))
1.0
& m=3,k=0
becomes 1
(because k<1
→ so r
becomes 1
; r.length()<m
(1<3
) → substring from index 0
to r.length()
(1
))
25.0
& m=4,k=4
becomes 25.00
(because k>=1
→ so r
becomes 25.00000
; r.length()>=m
(8>=4
) → r.contains(".")
→ r.length()>+m
(8>=4
) → substring from index 0
to m+1
(5
))
0
& m=1,k=0
stays 0
(because k<1
→ so r
stays 0
; r.length()>=m
(1>=1
) → !r.contains(".")
→ substring from index 0
to m
(1
))
for(i=r.length();i++<l;)
r+=0;
This puts trailing zeroes back again to the integer part if necessary.
r="12"
& R=1200.0
becomes r="1200"
r="1"
& R=10.0
becomes r="10"
r="8"
& R=80.0
becomes r="80"
return r.replaceAll(z+"$","");
And finally we return the result, after we've removed any trailing dots.
1203.
becomes 1203
5.
becomes 5
Can definitely be golfed by a couple hundred bytes, but I'm just glad it's working now. It already took a while to understand each of the cases and what was being asked in the challenge. And then it took a lot of trial-and-error, testing and retesting to get to the result above. And while writing this explanation above I was able to remove another ±50 bytes of unused code..
What do you mean by "the same number of places"? Is that the same as "the same number of significant figures"? If you want an edge case for addition,
999 + 2.00
. – Peter Taylor – 2016-08-25T07:14:32.207Surely
300 + 1 - 300
is a string of additions and subtractions, so doesn't need to be rounded until the end.(300 + 1) - 300
would be zero. – Neil – 2016-08-25T19:14:35.340