Given that I'd like to do the following calculation:
total = subtotal - discount
Because discount
might be greater than subtotal
, there is code like the following:
class Calculator
def initialize(subtotal: subtotal, discount: discount)
@subtotal = subtotal
@discount = discount
end
def total
[subtotal - discount, 0].max
end
private
def subtotal
@subtotal
end
def discount
@discount
end
end
When seeing the [subtotal - discount, 0].max
part or any similar code, I often have to pause and think.
Are there more elegant ways to handle this kind of calculation?
The negative?() is an inbuilt method in Ruby returns a boolean value. It returns true if the number is a negative one, else it returns false.
Negative numbers are stored using two's complement. This method takes advantage of how when you add 7 and it's negative complement -7, you get 0. (7 + -7 = 0).
Login [Register]Storing negative numbers, however, is legal, but the number will get "wrapped" to fit. For example, if you assign -1 to A, it will really hold 255. If you assign -2330 to BC, it will really hold 63206. Adding one plus the maximum value the register will hold gives you the value that will be stored.
I think your solution is essentially correct, and probably the most readable besides a small refactor. I might change it slightly like so:
def total
final_total = subtotal - discount
[final_total, 0].max
end
The ruby expression [final_total, 0].max
is essentially the traditional solution in mathematics for the same function: max {final_total, 0}
. The difference is just notation and context. Once you see this max expression once or twice you can read it as follows: "final_total, but at least zero".
Perhaps if you use this expression more than once you can add another at_least_zero
method or something like in Shiko's solution.
Some performance numbers:
user system total real
[i, 0.0].max 0.806408 0.001779 0.808187 ( 0.810676)
0.0 if i < 0.0 0.643962 0.001077 0.645039 ( 0.646368)
0.0 if i.negative? 0.625610 0.001680 0.627290 ( 0.629439)
Code:
require 'benchmark'
n = 10_000_000
Benchmark.bm do |benchmark|
benchmark.report('[value, 0.0].max'.ljust(18)) do
n.times do |i|
a = [-1*i, 0.0].max
end
end
benchmark.report('0.0 if value < 0.0'.ljust(18)) do
n.times do |i|
a = 0.0 if -1*i < 0.0
end
end
benchmark.report('0.0 if value.negative?'.ljust(18)) do
n.times do |i|
a = 0.0 if (-1*i).negative?
end
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With