Out-of-Control Rounding Errors

14

Background

You have recently been hired by a small accounting firm. The world of accounting is somewhat foreign to you, so you're not sure whether you are following all the professional guidelines. In particular, you don't know when you should round all those numbers, and in which direction, so most of the time you just wing it and hope for the best.

Input

Your input is a single string that represents a simple calculation. It contains some number of nonnegative integers delimited by the characters +-*/. The string reads from left to right, and the normal precedence rules are ignored, so "23+1*3/4" means "start with 23, add 1, multiply by 3, and divide by 4", the result being 18. The input will not contain numbers that start with 0 (except 0 itself), nor a division by zero.

Output

At each stage of the calculation, you can either round the result up or down to the nearest integer, or keep it as it is. Finally, you round either up or down to obtain a integer result. Your output is the list of integers that may result from such a calculation, sorted and without duplicates.

Rules

You can write either a full program or a function. The lowest byte count wins, and standard loopholes are disallowed.

Test Cases

"42" -> [42]
"2+0+4-0" -> [6]
"23+1*3/4" -> [18]
"5/2" -> [2,3]
"5/2+7/3*6-1" -> [17,18,19,23]
"23/2/2*30-170/3" -> [-7,-6,-2,-1,0,1,3,4]
"1/3*2*2*2*2*2*2" -> [0,16,20,21,22,24,32,64]
"1/3*9" -> [0,3,9]

Zgarb

Posted 2015-04-07T17:30:20.913

Reputation: 39 083

Does the program have to work for all possible inputs (regardless of number size), a limited size input, or only the test cases? – orlp – 2015-04-07T17:54:03.207

@orlp It should work at least when all input numbers and intermediate results are below, say, 10 million in absolute value. The accounting firm is small, after all. – Zgarb – 2015-04-07T18:01:50.310

Take note of the test case 1/3*9, which may fail if you use floating point numbers. – Claudiu – 2015-04-07T18:28:52.337

@Claudiu Thanks, I added it to the challenge. – Zgarb – 2015-04-07T18:31:56.967

Answers

4

J, 84 bytes

Starting from a 1 element list the function keeps all the possible intermediate numbers in the list by evaling the next expression and adding it's up and down rounded copies.

Will golf further and add explanation tomorrow. Can't find obvious ways to golf it more.

f=.3 :'/:~~.<.>((,>.,<.)@".@(":@],''x'',;@[))&.>/|.(>@{.;_2<\}.);:y rplc''/'';''%'''

Passes all the tests.

Usage:

   f '1/3*2*2*2*2*2*2'
0 16 20 21 22 24 32 64
   f '1/3*9'
0 3 9

Try it here.

randomra

Posted 2015-04-07T17:30:20.913

Reputation: 19 909

How do you deal with them as rationals instead of floats - is that built in to J? (Complete J noob here) – Claudiu – 2015-04-08T08:36:55.457

@Claudiu At every eval I force extended precision numbers (in this case rationals) by appending the letter x at the end of the list. – randomra – 2015-04-08T14:38:15.703

3

Python 2, 220 characters

import re,sys,math as m,fractions as f
X=f.Fraction
M=map
F=['+']+re.split("(\D)",sys.argv[1])
r=[X(0)]
while F:U=[eval('f.'+`n`+F[0]+F[1])for n in r];r=M(X,U+M(m.floor,U)+M(m.ceil,U));F=F[2:]
print sorted(set(M(int,r)))

It keeps a list of all possible numbers and at each step, generates three numbers for every number in the list, even if there are duplicates. Thus the run-time complexity is exponential. However, it works instantly for these small examples. Dupes are removed at the end.

It uses fractions.Fraction to do exact division, avoiding floating point inexactitudes.

Add 5 characters (r=map(X,g) --> r=set(map(X,g))) to dramatically increase performance.

Claudiu

Posted 2015-04-07T17:30:20.913

Reputation: 3 870

Here's an easy golf to start off with: \D is a predefined character class for matching non-digits

– Sp3000 – 2015-04-07T18:01:13.587

@orlp: Fixed now! (I think..) – Claudiu – 2015-04-07T18:32:46.587

@Claudiu: that should be either r"(\D)" or "(\\D)". Also, if you use Python 3, you can replace the indexing into F with starred assignment, e.g.: A,B,*F=F, use A and B instead of F[0] and F[1], and get rid of F=F[2:]. – Mac – 2015-04-09T03:00:37.507

@Mac: "\D" ends up working anyway and it's shorter. It's not a valid escape sequence so Python just includes the \ and D verbatim. Good Python3 tip actually, I'll check it out, though I'll have to replace backticks with repr() and turn the map result into a list. Starred assignment is something I wish Python 2 had.. – Claudiu – 2015-04-09T05:10:08.247

2

Python, 421 370 354 bytes

Sorry, please bear with me. I am really new to python (I was just looking for a language that supports fractiosn) and used all of the few tricks I knew for shortening the code but it is still a monster considering that there is a python solution of almost half the size. I learned a lot and thought I'd submit it anyway=)

New Version thanks to @kirbyfan64sos and @Zgarb

from fractions import*
from math import*
import re,operator as z
s=input()
N=re.split(r'[\*\/\-\+]',s)
O=re.split(r'[0-9]+',s)[1:-1]
d={'+':z.add,'-':z.sub,'*':z.mul,'/':z.truediv}
l=[int(N[0])]#list of inters up to now
for i in range(len(O)): #iterate over all operations
    n=set()
    for f in l:
        f=d[O[i]](f,Fraction(int(N[i+1])))
        n.update([floor(f),ceil(f),f])
    l=n
print(set(map(floor,n)))

Old Version

from fractions import Fraction as F
from math import floor,ceil
import re
s=input()
N=re.split(r'[\*\/\-\+]',s)   #Numbers
O=re.split(r'[0-9]+',s)[1:-1] #Operators
l=[int(N[0])]
for i in range(len(O)): #Iterate over all operators
    n=set()
    for f in l:           #Iterate over all possible numbers
        g=F(int(N[i+1]))
        o=O[i]
        if o=='/':
            f/=g
        elif o=='*':
            f*=g
        elif o=='-':
            f-=g
        else:
            f+=g
        n.add(floor(f))  #Add all possible numbers to a new set 
        n.add(ceil(f))   # the 'set' structure prevents from having multiple copies
        n.add(f)         # which is a really nice feature
    l=n                #repeat
print(set([floor(k) for k in n])) #also remove the unrounded ones

flawr

Posted 2015-04-07T17:30:20.913

Reputation: 40 560

For one thing, you could replace some of the space indents with tabs (it generally sucks, but works well in code golf: a tab == 1 character). You can also use a dict instead of several ifs (d={'+': operator.add, '-': operator.sub, ...}; d[op](a, b)). Also, [floor(k) for k in n] can be shortened to map(floor, n), and the n.add calls can become n.extend([floor(f), ceil(f), f]). – kirbyfan64sos – 2015-04-12T21:22:28.043

Wow thank you very much, I'm gonna try and implement them! I allready counted the indents as tabs but I had to convert them to spaces here. – flawr – 2015-04-12T21:31:41.210

You can also just use single spaces; they should work. – kirbyfan64sos – 2015-04-12T22:01:50.163

As far as I can see, you use F only once, so you could do from fractions import* and save some bytes. Same with math. Remove the spaces around =, they are unnecessary. Also, you should assign the input to s instead of hard-coding. – Zgarb – 2015-04-13T07:27:54.020

@flawr Remove every optional space. Also, you need to be able to accept any input. Use s=input() rather than s = "1/3*9", remove your comments, etc. – mbomb007 – 2015-04-13T21:17:25.553

Oh sorry, I pasted the wrong version, now corrected it. – flawr – 2015-04-13T21:36:08.150

1

Mathematica, 134

Union@Flatten@{Floor@#,Ceiling@#}&@ToExpression@StringReplace[#,x:("+"|"-"|"*"|"/"~~NumberString):>"//{Floor@#,#,Ceiling@#}"~~x~~"&"]&

alephalpha

Posted 2015-04-07T17:30:20.913

Reputation: 23 988

0

MATLAB, 283 chars

function[u]=w(s)
s=[' ' strsplit(regexprep(s,'\D',' $& '))];s=reshape(s,[2,size(s,2)/2]);o=s(1,:);d=cellfun(@str2num,s(2,:));a=d(1);for n=2:size(o,2)switch o{n}case'+';a=a+d(n);case'-'a=a-d(n);case'/'a=a/d(n);case'*'a=a*d(n);end;a=[ceil(a);a;floor(a)];end;u=unique(a(mod(a,1)==0))end

Ungolfed:

function [u] = WingitRound(i)
    i=[' ' strsplit(regexprep(i,'\D',' $& '))];
    i=reshape(i,[2,size(i,2)/2]);

    o=i(1,:);
    n=cellfun(@str2num,i(2,:));

    a=n(1);

    for n=2:size(o,2)
        switch o{n}
            case '+'
                a = a + n(n);
            case '-'
                a = a - n(n);
            case '/'
                a = a / n(n);
            case '*'
                a = a * n(n);
        end
        a = [ceil(a);a;floor(a)];
    end

    u=unique(a(mod(a,1)==0)));
end

While writing this, I realized that there is an even shorter way of doing this, which I will add once I finish writing it.

AJMansfield

Posted 2015-04-07T17:30:20.913

Reputation: 2 758

0

VBA, 347 bytes

Function OoCRE(inp As String)
ct = 0
i = 1
Do While i < Len(inp)
c = Mid(inp, i, 1)
If Not IsNumeric(c) Then
ct = ct + 1
If ct = 2 Then
inp = Round(Application.Evaluate(Left(inp, i - 1))) & Right(inp, Len(inp) - (i - 1))
i = InStr(1, inp, c)
ct = 1
End If
End If
OoCRE = Round(Application.Evaluate(inp))
i = i + 1
Loop
End Function

Alex

Posted 2015-04-07T17:30:20.913

Reputation: 369

1There is quite a lot of golfing to be done here, primarily removing superflous whitespace and choosing shorter var names – cat – 2015-12-08T01:23:33.833