34

I'm taking a course in Java and we haven't officially learned if statements yet. I was studying and saw this question:

Write a method called pay that accepts two parameters: a real number for a TA's salary, and an integer for the number hours the TA worked this week. The method should return how much money to pay the TA. For example, the call pay(5.50, 6) should return 33.0. The TA should receive "overtime" pay of 1.5 times the normal salary for any hours above 8. For example, the call pay(4.00, 11) should return (4.00 * 8) + (6.00 * 3) or 50.0.

How do you solve this without using if statements? So far I've got this but I'm stuck on regular pay:

public static double pay (double salary, int hours) {

     double pay = 0;

     for (int i = hours; i > 8; i --) {
         pay += (salary * 1.5);
     }
}
babyDev
  • 415
  • 5
  • 8
  • 12
    If you have already learned loops but not if-statements (which is weird btw) you can use `for(; condition; ) { …; break; }` like `if (condition) { … }` – Bergi Sep 01 '16 at 09:36
  • 6
    @Bergi: right, if this is supposed to be a puzzle with the questioner's hands tied, then that's a good way to untie them within the rules. If it's not supposed to be that kind of puzzle, then without knowing what *has* been taught I'm a bit stumped figuring out what answer the questioner's teacher is expecting. Hopefully *not* that. There are some good guesses in the answers :-) – Steve Jessop Sep 01 '16 at 10:08
  • 1
    I'm not sure what all the fuss is about not using an `if` statement. I see the question and think: Why would I use an `if`, that just makes my code longer/more complicated. When I use `if` I have to effectively re-create `min` and `max`. This type of calculation is what `min` and `max` are for. In many situations (e.g. a spreadsheet) those are definitely the preferred solution. – Makyen Sep 01 '16 at 15:34
  • 6
    I having a hard time figuring out why anyone would be asking to solve this without explaining `if`, yet having explained `for` loops and functions (with arguments). Why not explaining the more basic stuff first? Puzzling. – chi Sep 01 '16 at 16:09
  • 3
    Didn't you just miss a lecture? Loops before ifs are sure weird. – Kyslik Sep 02 '16 at 01:19

17 Answers17

82

To avoid direct use of flow control statements like if or while you can use Math.min and Math.max. For this particular problem using a loop would not be efficient either.

They may technically use an if statements or the equivalent, but so do a lot of your other standard library calls you already make:

public static double pay (double salary, int hours) {
     int hoursWorkedRegularTime = Math.min(8, hours);
     int hoursWorkedOverTime = Math.max(0, hours - 8);
     return (hoursWorkedRegularTime * salary) +
            (hoursWorkedOverTime  * (salary * 1.5));
}
NESPowerGlove
  • 5,496
  • 17
  • 28
  • 1
    I like this one. No comparisons at all. – zero298 Aug 31 '16 at 21:45
  • 9
    @zero298 Except [min](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Math.java#Math.min%28int%2Cint%29) and [max](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Math.java#Math.max%28int%2Cint%29) use ternary operators, which do do comparison. – Jorn Vernee Aug 31 '16 at 21:50
  • 1
    @JornVernee But that's under the hood. There are no comparisons in that snippet of source. – zero298 Aug 31 '16 at 21:51
  • 39
    I can't imagine many java courses teach the Math library before if statements. It's valid, but if I was a tutor reading this answer I would be suspicious. – js441 Aug 31 '16 at 21:55
  • 1
    @js441 Well all of these solutions require knowledge you would most likely learn after using ifs. OP's question text implies too that they may have looked ahead at a future question in their study materials. – NESPowerGlove Aug 31 '16 at 22:02
  • @js441 Very good point. Knowing what is available is important, but so is the context. For turning in classroom questions, the answer is always "by using what the class has already taught you." I've been in some classes that "taught" to search for an components of your answer online or in reference docs, and in others that actually graded down for using anything beyond the content of the textbook up to that point. (One HTML class was very literal with this, to the point where using alt text in an tag before you "learned" it lost points...) – newcoder Aug 31 '16 at 22:09
  • 2
    @newcoder this is why in my answer I just used two for loops. I thought since OP had demonstrated that it was a safe option. – js441 Aug 31 '16 at 22:12
  • 10
    There *are* branchless min and max. – johnchen902 Sep 01 '16 at 02:40
53

Since you've used a for loop, here's a solution just using two for loops.

public static double pay (double salary, int hours) {

    double pay = 0;

    for (int i = 0; i < hours && i < 8; i++) {
        pay += salary;
    }
    for (int i = 8; i < hours; i++) {
        pay += (salary * 1.5);
    }

    return pay;
}

This sums the salary for the regular hours up to 8, and then sums the salary for the overtime hours, where the overtime hours are paid at 1.5 * salary.

If there are no overtime hours, the second for loop will not be entered and will have no effect.

js441
  • 1,134
  • 8
  • 16
  • 1
    Maybe `i < hours && i < 8` in the first loop and `salary * 1.5` in the second would be clearer. – Bergi Sep 01 '16 at 09:38
  • 17
    I believe this is actually the answer that the teacher is looking for, tbh, though I think the logic of the teacher is rather dumb bcz all loops use some kind of branching statement in order to exit a loop. – Rabbit Guy Sep 01 '16 at 13:15
  • 1
    @rabbitguy I don't think the teacher has set "don't use if statements" as a requirement. The question just says "we haven't officially learned if statements yet" – Ben Aaronson Sep 02 '16 at 07:56
  • What about fractional hours as shown in the example above? This loop would say a TA that worked 5.5 hours should be paid 36 arbitrary currency. – Phylogenesis Sep 02 '16 at 08:19
  • @Phylogenesis hours is specified as an integer, so can't be fractional. Unless the function spec. is changed. – js441 Sep 02 '16 at 08:21
  • You're right. Completely misread the function signature. It's the salary that is fractional, not the number of hours. – Phylogenesis Sep 02 '16 at 08:38
  • I would do `final double overtimeSalary = salary * 1.5;` ahead of second loop and use that one, instead of using multiplying every loop. – Ped7g Sep 02 '16 at 11:58
20

There's a few ways you can go about this, but it's hard to know what's allowed (if you can't even use if).

I would recommend using a while loop:

double pay = 0;
while (hoursWorked > 8) {
    pay += (salary * 1.5);
    hoursWorked--;
}
pay += (hoursWorked * salary);

The reason why this works is it decrements your hoursWorked to a value that is guaranteed to be less than or equal to 8 (assuming hoursWorked and salary are both greater than 0). If hoursWorked <= 8, then it will never enter the while loop.

ihgann
  • 505
  • 3
  • 11
  • 1
    A `while` loop is basically an if statement, just with a jump back at the end. You could also do: `int otherHours = hoursWorked - 8; while(otherHours > 0) { pay += otherHours * salary * 1.5; break; } ...` – Jorn Vernee Aug 31 '16 at 21:33
  • 2
    Everything is essentially syntactic sugar for `if` in these answers. There isn't one *correct* answer, but these types of questions are just trying to get you to be creative with other approaches. – ihgann Aug 31 '16 at 21:36
  • 1
    I wasn't trying to criticize, just adding to the answer ;). – Jorn Vernee Aug 31 '16 at 21:37
20

If you really want to get hacky, you could use bitwise operators:

int otherHours = hours - 8;
int multiplier = (~otherHours & 0x80000000) >>> 31;
otherHours *= multiplier;

return otherHours * 0.5 * salary + hours * salary;

So basically, if otherHours is negative, there should be no overpay. We do this by selecting the sign bit of otherHours and shifting it to the least significant bit (with 0 padding) to mean either 1 or 0. After first negating it (if sign bit is 1, multiplier should be 0).

When you multiply this with otherHours it will be 0 in the case there are less than 8 hours, so as not to accidentally subtract any pay, when doing the final calculation.

Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93
  • 16
    I have a feeling that if the OP cannot use if statements, the teacher might get a bit suspicious if the student suddenly becomes a bit shifting magician :) – noobcoder Sep 01 '16 at 22:24
6

Just for the record, here is a solution quite close to where you were stopped :

public static double pay (double salary, int hours) {
     double pay = salary * hours;
     for (int i = hours; i > 8; i --) {
         pay += salary * 0.5;
     }
}
merours
  • 4,076
  • 7
  • 37
  • 69
6

You can simply use a ternary operator ?::

 pay = hours*salary + ((hours > 8) ? (hours-8)*salary*0.5 : 0); 

— pay a standard salary for the whole time worked, plus 50% for time above 8 hours (if any).

CiaPan
  • 9,381
  • 2
  • 21
  • 35
  • 2
    I voted this up, ternary operator is not an IF, an IF is an IF. the teacher never said no comparisons. – montelof Sep 01 '16 at 16:23
  • Agreed, any answer to this question is going to be *cute* to some degree or another, because real code would just use an if. – Jason M Sep 01 '16 at 18:28
  • 1
    @JasonM I disagree--the accepted answer using Min and Max is a good way of writing it for those new to coding as it's very descriptive of what's going on--and it has no if. – Loren Pechtel Sep 02 '16 at 04:54
  • Fair enough, my immediate thought on reading the question title was very similar to the accepted answer. I should rather say that real code that a first year student would come up with would use an if - because that is the most obvious way to distinguish between overtime pay and regular pay. – Jason M Sep 02 '16 at 18:13
5

A cast to int can be abused for this purpose.

Note that the function

f(x) = 10/9 - 1/(x+1) = 1 + 1/9 - 1/(x+1)

is between 0 and 1 (exclusive) for 0 <= x < 8 and between 1 and 1.2 for x >= 8. Casting this value to int results 0 for x < 8 and in 1 for x >= 8.

This can be used in the calculation of the result:

public static double pay(double salary, int hours) {
    int overtime = (int)(10d/9d - 1d/(hours+1));
    return salary * (hours + 0.5 * overtime * (hours - 8));
}
fabian
  • 80,457
  • 12
  • 86
  • 114
4

Here's a way to do it using truncation from integer division, something that you probably have learnt at the start of java courses. Essentially the solution is a one liner that does not need if, loops, comparisons, libraries.

public static double pay(double salary, int hours) {

    //Pay all hours as overtime, and then subtract the extra from the first 8 hours
    double p1 = (hours * 1.5 * salary) - (4 * salary); 

    //In the case where the TA works for less than 8 hours, 
    //subtract all the extra so that ultimately, pay = salary * hours
    double p2 = (hours * 0.5 * salary) - (4 * salary); 

    //When theres no overtime worked, m equals to 1. 
    //When there are overtime hours, m is equals to 0.
    int m = (8 + 7) / (hours + 7);

    //When there are overtime hours, pay is simply equal to p1. 
    //When there are no overtime hours, p2 is subtracted from p1.
    return p1 - m*p2; 
}
Lincoln Cheng
  • 2,263
  • 1
  • 11
  • 17
3

A solution which does not use any conditional(implicit or explicit)

Practically, you need to calculate hours * rate but if you have overtime then you need to add a bonus of the form overtime_hours * overtime_rate

in pseudo-code:

//you need to return:
hours * rate + is_overtime * overtime_time * overtime_rate

where

is_overtime = ceiling ( x / (x+1))  # this will be zero when x == 0, in rest 1 
x = integer_division(hours, 8)   # x == 0 if no overtime, in rest a positive integer
overtime_time = hours - 8
overtime_rate = (1.5 - 1) * rate = 0.5 * rate
valentin
  • 3,498
  • 15
  • 23
1
(((hours/8)-1)*8 + hours%8)*salary*0.5 + (hours*salary)
                   overtime*salary*0.5 + (hours*salary)

(((   11/8 -1)*8 +    11%8)*     4*0.5 + (   11*     4) = 50
((      1  -1)*8 +       3)*         2 +             44 = 50
((          0)*8 +       3)*         2 +             44 = 50
((             0 +       3)*         2 +             44 = 50
                                     6 +             44 = 50

So suppose we have (17 hours, 4 salary)

(((17/8)-1)*8 + 17%8)*4*0.5 + 17*4 = 86
(    (2 -1)*8 +    1)*4*0.5 +   68 = 86
           (8 +    1)*2     +   68 = 86
                    9*2     +   68 = 86
                     18     +   68 = 86

17-8=9 is overtime

9*4*1.5 + 8*4 = 9*6 + 32 = 54 + 32 = 86

Sergey Morozov
  • 4,528
  • 3
  • 25
  • 39
1

You could creatively use a while statement as an if statement

while(hours > 8){
  return ((hours - 8) * salary * 1.5) + (8 * salary);
}
return hours * salary;
Altainia
  • 1,347
  • 1
  • 7
  • 11
0

Plenty of good and more efficient answers already, but here's another simple option using a while loop and the ternary operator:

double pay = 0.0;

while(hours > 0){
    pay += hours > 8 ? wage * 1.5 : wage;
    hours--;
}       
return pay;
blakem
  • 21
  • 4
0

Using tanh to decide whether the hours are below 8 or not:

public static double pay (double salary, int hours) {
    int decider = (int)(tanh(hours - 8) / 2 + 1);

    double overtimeCompensation = 1.5;
    double result = salary * (hours * (1 - decider) + (8 + (hours - 8) * overtimeCompensation) * decider);
    return result;
}

The decider variable is 0 when hours is less than 8, otherwise 1. The equation basically contains two parts: the first hours * (1 - decider) would be for hours < 8, and (8 + (hours - 8) * overtimeCompensation) * decider for when hours >= 8. If hours < 8, then 1 - decider is 1 and decider is 0, so using the equations first part. And if hours >= 8, 1 - decider is 0 and decider is 1, so the opposite happens, the first part of the equation is 0 and the second is multiplied by 1.

Dániel Nagy
  • 11,815
  • 9
  • 50
  • 58
0

public static double pay (double salary, int hours) {

    int extra_hours = hours - 8;
    extra_hours = extra_hours > 0 ? extra_hours : 0;


    double extra_salary = (salary * 1.5) * extra_hours;
    double normal_salary = extra_hours > 0 ? salary * 8 : salary * hours;

    return normal_salary + extra_salary;
}
fjgarzon
  • 44
  • 4
-1

You can use ternary operator.

public static double pay(double salary, int hours){ //get the salary and hours return hours>8?(1.5*hours-4)*salary:hours*salary; }

-2

Some programming languages have explicit pattern-matching features. So for example, in XSLT, a given template will fire if the node being processed matches a given XPATH query better than other templates in your programme.

This kind of "declarative" programming is a level of abstraction higher than what you have in Java, but you still have the switch statement, which gives you the ability to control your flow with a "matching" approach rather than using explicit if-else constructs.

public static double pay (double salary, int hours) {

 double pay = 0;

 for (int i = hours; i > 0;i--){ 
     switch (i){
         case 1:
         case 2:
         case 3: 
         case 4: 
         case 5:
         case 6:
         case 7:            
            pay += (salary);
            break;
         default:
            pay += (salary * 1.5);
            break;
     }
 }
 return pay;
}

Having said that, for your specific example, what you really do need is an if statement. The switch approach will work, but it's a bit contrived.

Dominic Cronin
  • 6,062
  • 2
  • 23
  • 56
  • 1
    Sheesh... whoever that was with the drive-by downvote... how about some constructive feedback? – Dominic Cronin Sep 02 '16 at 09:53
  • Another drive-by downvote without an explanation. If there's a problem with my answer, please say so. – Dominic Cronin Sep 04 '16 at 17:44
  • Yet another drive-by downvote... When will I ever learn why using switch constitutes such a bad answer? Sure - you'd never code it like this - you'd use an if - but as the questioner specifically wants to avoid that, switch is as good as any of the other answers. – Dominic Cronin Sep 19 '16 at 15:17
-4

In Tsql

DECLARE @amount float = 4.00 , @hrs int = 11

DECLARE @overtime int , @overeight bit

SET @overeight = ((@hrs/8) ^ 1)

SET @overtime = CEILING((@hrs%8) / ((@hrs%8) + 1.0)) * ~@overeight

--SELECT @hrs * @amount

SELECT ((@hrs-(@hrs%8 * ~@overeight)) * @amount) + ( @overtime * (@hrs%8 * (@amount * 1.5)))

(from Valentin above)

Jason
  • 1