16
\$\begingroup\$

You and a friend walk into a bar. The bartender treats you well, so you decide to tip him. So you pull out your trusty pocket computer and write a quick program to calculate a tip for you since it has no built in calculator. But wait! Your operator keys are broken! Your task is to calculate a 20% tip for any given input amount. The test inputs will be in the form of xx.xx e.g. 20.96. Here are the rules:

  • No use of the following in mathematical form operators: + - * / % (Thanks Wally West)
  • No use of built in percent functions or APIs
  • No accessing the network
  • No use of eval or similar
  • No Windows Calculator (yes people have answered with it before)
  • No built in tip functions (Not that any language would have one)

Output should be rounded up to two decimal places.

Score is based on length in bytes.

-20% if your program can accept any tip amount. Input for amount will be given in the form of xx e.g. 35 not 0.35

\$\endgroup\$
20
  • 5
    \$\begingroup\$ @mniip: A tip is a bizarre extra amount of money you're expected to pay at a restaurant that isn't part of the official price. I don't know if they do that in Russia. \$\endgroup\$ Mar 12, 2014 at 7:36
  • 5
    \$\begingroup\$ Oh, that new generation... I would rather use paper and pen if I can't mentally divide a number by 5. \$\endgroup\$
    – VisioN
    Mar 12, 2014 at 8:45
  • 4
    \$\begingroup\$ @VisioN Note: dividing by five is equivalent to dividing by 10 then multiplying by 2, and both should be very easy mental operations. \$\endgroup\$
    – user12205
    Mar 12, 2014 at 9:05
  • 1
    \$\begingroup\$ Can someone clarify the rules: Is using the forbidden operations allowed if you don't use those operators? Is multiplication allowed if you don't use *? \$\endgroup\$
    – Ypnypn
    Mar 12, 2014 at 20:44
  • 2
    \$\begingroup\$ @TheDoctor is it really a duplicate though? Because it's not adding. \$\endgroup\$
    – Milo
    Mar 12, 2014 at 21:50

17 Answers 17

17
\$\begingroup\$

Javascript 81

EDIT : The result is now a real rounding, not truncated.

Do not use ANY math here, just string manipulation and bitwise operators.
All the + char are string concatenation, or string to float conversion.
Works for any xx.xx input and outputs in (x)x.xx format

s='00'+(prompt().replace('.','')<<1);(+s.replace(/(.*)(...)/,'$1.$2')).toFixed(2)

Trick explained : 20% is divided by 10 and multiplied by 2.

  • Make a division by 10 by moving the dot to the left (string manipulation)
  • Multiply each part by 2 by moving bits to the left (bitwise operation)
\$\endgroup\$
8
  • \$\begingroup\$ When input is 99.99, this returns 19.998, so it's not "rounded up to two decimal places" as per the OP. \$\endgroup\$
    – Geobits
    Mar 12, 2014 at 18:51
  • 2
    \$\begingroup\$ Umm this is a long solution, but it has 12 upvotes. What is going on with code-golf's voting? \$\endgroup\$
    – Justin
    Mar 13, 2014 at 5:16
  • \$\begingroup\$ @Quincunx Yes you are right its not popularity contest! \$\endgroup\$ Mar 13, 2014 at 5:48
  • \$\begingroup\$ Since it is a code-golf, I will probably not win, but this doesnt mean that people can't like my solution... Often, code-golf winners are not those with the most upvotes. \$\endgroup\$
    – Michael M.
    Mar 13, 2014 at 6:56
  • \$\begingroup\$ @n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳, absolutly ! Thank you for pointing this out. Fixed now :) \$\endgroup\$
    – Michael M.
    Mar 13, 2014 at 12:54
6
\$\begingroup\$

J (9 characters)

A short answer in J:

^.(^.2)^~ 32
\$\endgroup\$
2
  • 1
    \$\begingroup\$ ...and I do not speak J. What does it say? \$\endgroup\$ Mar 13, 2014 at 13:22
  • \$\begingroup\$ It says: compute 32 raised to power log(2); usually, exponent is at the right of ^ but here ~ indicates the order should be reversed; (^.2)is log(.2), and the initial ^. which is executed last is the logarithm of everything compute dpreviously: thus log(32^log(.2)) \$\endgroup\$ Mar 13, 2014 at 13:29
4
\$\begingroup\$

Pure bash, 50 43 chars

Pretty sure this'll be beat, but here's a start:

a=$[10#${1/.}<<1]
echo ${a%???}.${a: -3:2}

As per the comments:

  • / is not a division operator here, it is part of a pattern substitution parameter expansion
  • % is not a modulo operator here, it is part of a parameter expansion
  • - is not a subtraction operator here, it is a negation, which was allowed as per the comments

Output:

$ ./20pct.sh 0.96
.19
$ ./20pct.sh 20.96
4.19
$ ./20pct.sh 1020.96
204.19
$ 
\$\endgroup\$
10
  • \$\begingroup\$ I think I see a forward slash in there. \$\endgroup\$
    – Milo
    Mar 12, 2014 at 6:22
  • \$\begingroup\$ @Milo that / is part of a bash parameter expansion and not a division operator. Allowed or not? \$\endgroup\$ Mar 12, 2014 at 6:24
  • 1
    \$\begingroup\$ I would allow it, it's not a mathematical operator in this case... \$\endgroup\$ Mar 12, 2014 at 6:25
  • 1
    \$\begingroup\$ -3 acts as a negation not a subtraction... @Milo, you need to account for negation and other ways that these operators can be used aside from their mathematical equivalents... for example + can be used as a concatenator in JavaScript... Can I make a suggestion? Change "No use of the following operators: + - * / %" to "No use of the following operators in their mathematical form: + - * / %" \$\endgroup\$ Mar 12, 2014 at 6:30
  • 2
    \$\begingroup\$ @WallyWest To play devil's advocate, the negation here is still a mathematical operator. Perhaps "No use of the following operators in their non-unary mathematical form: + - * / %" ;-) \$\endgroup\$ Mar 12, 2014 at 6:33
4
\$\begingroup\$

Python 98*0.8=78.4

d=`len('X'*int("{}{:>02}".format(*(raw_input()+".0").split('.')))*2)`;print'%s.%s'%(d[:-3],d[-3:])

Python 74 (without bonus)

d=len('X'*int(raw_input().replace('.',''))*2);print'%s.%s'%(d[:-3],d[-3:])

Note

  • + is used for string concatenation
  • * used to create copies of string

Ungolfed

def tip():
    amount = raw_input()
    #Add an extra decimal point so that we can accept integer
    #amount
    amount += ".0"
    #Split the integer and decimal part
    whole, frac = amount.split('.')
    #Multiply amount by 100 :-)
    amount = "{}{:>02}".format(whole, frac)
    #Create amount copies of a character
    st = 'X'*amount
    #Double it
    st *= 2
    #Calculate the Length
    d = len(st)
    #Display the result as 3 decimal fraction
    print'%s.%s'%(d[:-3],d[-3:])

Note

In spirit of the question, I believe the following solution though follows all rules of the question is an abuse

Python 41

print __import__("operator")(input(),0.2)

Finally

If you insist that the mathematical symbols are forbidden, her is a 90 character solution

Python 90 (without any mathematical symbol)

print' '.join(str(int(raw_input().replace(".",""))<<1)).replace(' ','.',1).replace(' ','')
\$\endgroup\$
5
  • 1
    \$\begingroup\$ As already said, * and + are broken KEYS (whatever you use them for). \$\endgroup\$ Mar 12, 2014 at 9:58
  • 2
    \$\begingroup\$ @ברוכאל: No use of the following in mathematical form operators: + - * / % (Thanks Wally West). In other words, the question needs to be rephrased. And I have not used them in mathematical form \$\endgroup\$
    – Abhijit
    Mar 12, 2014 at 10:00
  • \$\begingroup\$ OK, but in that case, the downvote of the APL solution was'nt really fair, since no other solution has been downvoted for that reason. \$\endgroup\$ Mar 12, 2014 at 10:01
  • \$\begingroup\$ Your answer is going to have problem with miniscule amount of money, e.g. 0.05. \$\endgroup\$ Mar 13, 2014 at 11:37
  • \$\begingroup\$ You can use `input` (backticks included) instead of raw_input. \$\endgroup\$
    – nyuszika7h
    Jun 12, 2014 at 21:12
4
\$\begingroup\$

APL (9 characters, new code with 7 characters, fixed with 13 characters)

Compute 20% of the given amount.

{⍟⍵*⍨*.2} 32

In APL * is the exponential operator.

Try it online.

But why use a function for that? See this new version:

⍟(*.2)* 32

Try it online.

Please, before downvoting, the * is not forbidden as a KEY and it doesn't mean multiplication here.

OK, here is the version rounding to 2 decimals (Dyalog APL):

⎕PP←2⋄⍟(*.2)* 32.01
\$\endgroup\$
12
  • 2
    \$\begingroup\$ Exponentiation is still a mathematical operation, and * is disallowed in any mathematical form as per the rules of the challenge. \$\endgroup\$
    – Tony Ellis
    Mar 12, 2014 at 9:26
  • \$\begingroup\$ Exponentiation is not fobidden here and other solutions actually use it, but you are right concerning the fact that * is forbidden as a key and not as its mathematical meaning. \$\endgroup\$ Mar 12, 2014 at 9:36
  • \$\begingroup\$ @Tony H. Please, could you remove your downvote, since obviously all comments in other solutions seem to finally accept such KEYS as long as they aren't used for the forbidden mathematical operations in the initial list. \$\endgroup\$ Mar 12, 2014 at 10:03
  • 1
    \$\begingroup\$ @ברוכאל It's my understanding that usage of an asterisk in any way that pertains to a mathematical meaning is forbidden, that includes exponentiation. Every other answer thus-far that uses exponents is in a language that uses the caret (^) for such an operator, or a function call is used. There is no specification by the OP that * is disallowed only if it's used in the context of multiplication. I suspect it would be helpful if OP were to rule on this, to settle which one of us is misinterpreting the rules of the challenge and to clear up the confusion. \$\endgroup\$
    – Tony Ellis
    Mar 12, 2014 at 14:14
  • 1
    \$\begingroup\$ Perhaps × and ÷ are allowed, those are not listed. \$\endgroup\$
    – marinus
    Mar 12, 2014 at 17:05
4
\$\begingroup\$

R, 30 27 36 34

Updated to round to 2 decimal places

Saved 2 characters thanks to plannapus

Creates a vector from 0 to x and takes the 2nd element.

a=function(x)round(seq(0,x,l=6)[2],2)

Example:

> a(20.36)
[1] 4.07

Further explanation:

seq(0,x,len=6)

creates a vector of length 6 from 0 to the input value x, with the values in between equally spaced.

> seq(0,5,len=6)
[1] 0 1 2 3 4 5

The first value is then 0%, the second 20%, third 40%, etc.

\$\endgroup\$
7
  • \$\begingroup\$ Wait... what ? Please give more details of how it works for those (like me) who don't know R. \$\endgroup\$
    – Michael M.
    Mar 12, 2014 at 18:50
  • \$\begingroup\$ @Michael updated my answer to explain the code \$\endgroup\$
    – Rift
    Mar 12, 2014 at 20:27
  • \$\begingroup\$ Your solution doesn't do any rounding. \$\endgroup\$ Mar 13, 2014 at 12:02
  • \$\begingroup\$ @n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ you are right (but most solutions aren't rounding anything). 9 extra characters would do "normal" rounding. Only rounding up would be way harder... \$\endgroup\$
    – Rift
    Mar 13, 2014 at 13:11
  • \$\begingroup\$ @Rift: I have tested several answers (mostly Python, JS and Perl, since they are easily available) and most of them do rounding. None of those I have tested rounds up, though. \$\endgroup\$ Mar 13, 2014 at 13:20
3
\$\begingroup\$

dc + sed + pipes (44 characters)

My third answer (one APL, one J, and now one with the old and venerable dc).

dc -e 5o?p|sed -e 's/\(.\)$/.\1/'|dc -e 5i?p

It will ask for an INTEGER input and compute 20% with a tricky way. Input is converted to base 5 (easy to do with many other tools but wait...); a dot is appended before the last digit with sed (unfortunately, dc can't handle strings very well), and THEN, dc converts back from base 5 on a float number (not all tools can do that).

\$\endgroup\$
2
  • 1
    \$\begingroup\$ "INTEGER input" doesn't meet the spec. I think you can fix it by changing your sed expression: dc -e 5o?p|sed 's/\(.\)\./.\1/'|dc -e 5i?p. With this and removing seds -es, this is 42. But the result is still not to spec (not 2 decimal places) \$\endgroup\$ Mar 12, 2014 at 15:42
  • \$\begingroup\$ @DigitalTrauma. You are right. But this solution was actually rather a proof of concept and I was more interested by the unusual property of base conversion in dc (and also in bc). Thank you for your comment and your fix in sed. \$\endgroup\$ Mar 12, 2014 at 15:46
2
\$\begingroup\$

Python, 88

This is no where near short, but this demonstrate how a normal mental division by five should be done. This assumes that the input must be of the form xx.xx

import math
a,b=raw_input().split('.')
print'%.2f'%math.ldexp(float(a[0]+'.'+a[1]+b),1)

Or for input of any length, an addition of 3 characters is required.

import math
a,b=raw_input().split('.')
print'%.2f'%math.ldexp(float(a[:-1]+'.'+a[-1]+b),1)

Explanation: We take the input as string, then move the decimal point one place forward (dividing by 10). We then cast it to a float, and use the ldexp function to multiply it by 2.

Note that in this answer, the + are string concatenation operators, and the % are used to format print.

If you insist on not using any of these characters, here is a 159 character solution:

import math
a,b=raw_input().split('.')
l=[a[0]]
l.append('.')
l.append(a[1])
l.append(b)
c=str(math.ldexp(float(''.join(l)),1))
print ''.join([c[0:2],c[2:4]])
\$\endgroup\$
3
  • \$\begingroup\$ Already said for many other answers, but I really don't think that + and - are allowed since the KEYS are broken (whatever they mean in your piece of code). \$\endgroup\$ Mar 12, 2014 at 9:59
  • \$\begingroup\$ @ברוכאל: See my response to your comment in my answer \$\endgroup\$
    – Abhijit
    Mar 12, 2014 at 10:01
  • \$\begingroup\$ Used your .split('.') in my answer, chopped one char \$\endgroup\$
    – user80551
    Mar 12, 2014 at 10:34
2
\$\begingroup\$

dc + sed -- 45 * 0.8 = 36

(Inspired by the answer by ברוכאל)

  • Handles any tip amount (integer or float)

Example runs (input is accepted via STDIN):

$ dc -e 5o?.0+p|sed 's/\(.\)\./.\1/'|dc -e 5i?p
42
8.400
$ dc -e 5o?.0+p|sed 's/\(.\)\./.\1/'|dc -e 5i?p
20.96
4.1920
\$\endgroup\$
3
  • \$\begingroup\$ The .0+ idea is great; I voted up for your solution inspired by mine, but some may tell you are using addition; maybe some workaround? I tried dc -e 9k5o?v2^p but it is 2 characters more. \$\endgroup\$ Mar 12, 2014 at 17:42
  • \$\begingroup\$ @ברוכאל I didn't realize that the + even in this context might qualify as addition. \$\endgroup\$
    – devnull
    Mar 12, 2014 at 18:10
  • \$\begingroup\$ @ברוכאל I'll delete this one. Request you to modify your so that it could handle floats and qualify for -20%! \$\endgroup\$
    – devnull
    Mar 12, 2014 at 18:12
2
\$\begingroup\$

Mathematica, 19

Log[(E^.2)^Input[]]

No use of the mathematical operators of +, -, *, /, or %. Uses properties of logarithms to calculate the answer; when you simplify the expression, you get .2*Input[]

With the bonus (30 * 0.8 = 24):

Log[((E^.01)^Input[])^Input[]]

Input the percentage first, then the amount.

When you simplify the expression, you get Input[]*Input[]*.01.

Thanks to ברוכאל for help with shortening the code.

\$\endgroup\$
1
  • 3
    \$\begingroup\$ Are you really sure it can't be simplified? To me Log[E^2] or Log[E^Input[]] can be written more shortly. Even 10^-1 can be written .01 (though I don't use Mathematica but I am pretty sure it can). The whole expression (E^10^-1)^Log[E^2] seems to be something like E^.2, isn't it? \$\endgroup\$ Mar 12, 2014 at 8:20
1
\$\begingroup\$

TI-89 Basic - 39 * 0.8 = 31.2

Input y:Input x:Disp ln(((e^y)^.01)^x))

Works by inputting the two numbers, then using the logarithm properties to compute x * y / 100.

If I can assume input from placement in global variables x and y, then this is much shorter, for a score of 17 * 0.8 = 13.6:

ln(((e^y)^.01)^x)

Without bonus (12):

ln((e^.2)^x)

But if it needs to be wrapped in a function, then this works (38 chars, for 30.4):

:f(x,y):Func:ln(((e^y)^.01)^x):EndFunc
\$\endgroup\$
5
  • \$\begingroup\$ Please, see my comments to your other solution (in Mathematica); this code can really be simplified much. \$\endgroup\$ Mar 12, 2014 at 8:24
  • \$\begingroup\$ Input x,y ??? Works in 83/84. \$\endgroup\$
    – Timtech
    Mar 12, 2014 at 11:09
  • \$\begingroup\$ @Timtech Doesn't work in 89, but maybe I can do something like Input{x,y} (does 83/84 have ln?) \$\endgroup\$
    – Justin
    Mar 12, 2014 at 18:51
  • \$\begingroup\$ @Quincunx Yes, see my new answer. \$\endgroup\$
    – Timtech
    Mar 12, 2014 at 20:20
  • \$\begingroup\$ žr¹msmžr.n - shameless port of this code into 05AB1E. \$\endgroup\$ Apr 11, 2017 at 19:21
1
\$\begingroup\$

PHP 107 *.8 = 85.6

can't really run for code-golf with PHP, but at least I can operate on strings. accepts both numbers as command-line arguments.

<? @$r=strrev;unset($argv[0]);echo$r(substr_replace($r(str_replace('.','',array_product($argv)))),'.',2,0);

had to reverse it twice since I can't use -2 :(

\$\endgroup\$
1
\$\begingroup\$

Python, 81 80 89 characters

a,b=map(str,raw_input().split('.'));c=str(int(a+b)<<1).zfill(4);print c[:-3]+'.'+c[-3:-1]

Explanation

x = raw_input()       # say 20.96
a , b = x.split('.')  # a = 20 , b = 96
c = a + b             # c = '2096'      # string concatenation , multiplying by 100
d = int(c)<<1         # multiply by 2 by bitshift left , c = 4096
e = str(d).zfill(4)   # zfill pads 0's making the string 
                      # atleast 4 chars which is required 
                      # for decimal notation next

#     divide by 1000 (4) + . (4.) + fraction rounded to 2 decimals (4.09)
print        e[:-3]      +   '.'  +              e[-3:-1]

Technically, this is cheating as it truncates to two decimals rather than rounding it but I can argue that it rounds down(Better for you, less tip).

\$\endgroup\$
3
  • \$\begingroup\$ I think you can do [-3:] instead of [-3:-1] \$\endgroup\$
    – Justin
    Mar 12, 2014 at 18:53
  • \$\begingroup\$ @Quincunx Can't , the -1 is to truncate to two decimals. \$\endgroup\$
    – user80551
    Mar 13, 2014 at 3:42
  • \$\begingroup\$ @n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ Yep, fixed it. \$\endgroup\$
    – user80551
    Mar 13, 2014 at 11:36
1
\$\begingroup\$

JavaScript 251 (forced restriction: no +, -, *, ? or % in any manner)

While I know this won't win, I figured I'd try to get brownie points through taking a very strict approach and not even think about using the restricted operators in any shape or form... as a result, I came up with this beauty...

A=(x,y)=>{for(;x;)b=x^y,x=(a=x&y)<<1,y=b;return y};D=(x,y,i=x,j=0)=>{for(;i>=y;)i=A(i,A(~y,1)),j=A(j,1);return j};alert(parseFloat((x="00".concat(String(D(prompt().replace(".",""),5)))).substr(0,A(x.length,y=A(~2,1))).concat(".").concat(x.substr(y))))

I used bitwise operations to create the Add function A, and then started chaining up from there:

The integer division function D used a series of Negated Addition (subtraction in the form of A(x,A(~y,1) over a loop; the rest is string manipulation and concatenation so as to avoid using + concatenation operators...

The number must be provided in decimal form with two decimal places for this to work...

\$\endgroup\$
1
  • \$\begingroup\$ Ah, it will now... though if I had to keep to the new rules, I had to expand on it... 251 bytes now... \$\endgroup\$ Mar 13, 2014 at 12:38
1
\$\begingroup\$

Perl, 50 60 bytes

$_=<>;s|\.||;$_<<=1;$_="00$_";m|\d{3}$|;printf'%.2f',"$`.$&"

The input is expected at STDIN. It must contain the decimal separator with two decimal digits. The output is written to STDOUT.

Update: The step $_<<=1 removes leading zeroes. Therefore m|\d{3}$| would not match for bills < 1. Therefore ten bytes $_="00$ were added, Now even 0.00 works.

Examples:

  • Input: 20.96, output: 4.19
  • Input: 12.34, output: 2.47

Ungolfed version:

$_=<>;
s|\.||; # multiply by 100
$_ <<= 1; # multiply by 2
$_="00$_";
m|\d{3}$|; # divide by 1000
printf '%.2f'," $`.$&"

First the number is read from STDIN. Then the decimal dot is removed, that is multiplied with 100. Then the amount is doubled by a shifting operator. Then the decimal dot is reinserted and the result is printed and rounded to two decimal digits.

50 bytes, if bill ≥ 1:

If x.xx is greater or equal than 1.00, then 10 bytes can be removed:

$_=<>;s|\.||;$_<<=1;m|\d{3}$|;printf'%.2f',"$`.$&"
\$\endgroup\$
0
1
\$\begingroup\$

JavaScript (ES6) (Regex) 142

Regex is great and it can do many things. It can even do maths!

a=('x'.repeat(prompt().replace('.', ''))+'xxxx').match(/^((x*)\2{99}(x{0,99}))\1{4}x{0,4}$/);c=a[3].length;alert(a[2].length+'.'+(c<10?'0'+c:c))

Readable version:

function tip(m) {
    var s = 'x'.repeat(m.replace('.', '')) + 'xxxx';
    var a = s.match(/^((x*)\2{99}(x{0,99}))\1{4}x{0,4}$/);
    var c = a[3].length;
    if (c < 10) c = '0' + c;
    return a[2].length + '.' + c;
}

The tip() function expects String argument, rather than Number.

All instances of *, /, + are not related to math operations.

  • + is string concatenation in all instances it is used.
  • * is part of RegExp syntax
  • / is the delimiter of RegExp literal

The input must use . as decimal point, and there must be 2 digits after decimal point.

Stack Snippet

<button onclick = "a=('x'.repeat(prompt().replace('.', ''))+'xxxx').match(/^((x*)\2{99}(x{0,99}))\1{4}x{0,4}$/);c=a[3].length;alert(a[2].length+'.'+(c<10?'0'+c:c))">Try it out</button>

\$\endgroup\$
0
\$\begingroup\$

Haskell 32 Chars -20% = 25.6 Chars

t y x=log $(^x)$(**0.01)$ exp y

Abuses the fact that exponents become multiplication in logarithms.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.