Having trouble with a Ruby rounding error - ruby

I can't for the life of me figure out why my: generate_receipt method is returning 54.9 for my "imported dinner plates item" when it should equal 54.65. I wrote RSpec tests to confirm that the array is indeed returning the correct values.
47.50 + 4.75 + 2.40 = 54.65
Why is it returning 54.9 instead of 54.65?! Where is this rounding up occurring? How do I get it to return the correct value? I'm stumped.
describe :calcualte_sales_tax do
it "should return the correct array" do
calculate_sales_tax(#receipt).should eq([0, 4.75])
end
end
describe :calculate_import_tax do
it "should return the correct array" do
calculate_import_tax(#receipt).should eq([0.50, 2.40])
end
end
#receipt = {
"1"=>{:item=>"imported chocolates", :price=>10.00, :quantity=>1},
"2"=>{:item=>"imported dinner plates", :price=>47.50, :quantity=>1}
}
def generate_receipt(receipt)
n = 0
while n < receipt.length
receipt["#{n+1}"][:price]+=calculate_sales_tax(receipt)[n]
receipt["#{n+1}"][:price]+=calculate_import_tax(receipt)[n]
n+=1
end
receipt
end
def calculate_import_tax(receipt)
taxes = []
receipt.each do |k,v|
if (v[:item] =~ /imported/)
subtotal = v[:price]
# v[:price]+=(((((5 * subtotal)/100)*20.ceil) / 20.0))
# taxes<<(5 * subtotal)/100
taxes<<((5 * subtotal/100)*20).ceil/20.0.round(2)
else
taxes<<0
end
end
taxes
end
def calculate_sales_tax(receipt)
tax_free_items = ["book", "chocolate bar", "chocolates", "pills"]
taxes = []
receipt.each do |k,v|
if (v[:item] =~ /chocolate\s/) ||
(v[:item] =~ /chocolates/) ||
(v[:item] =~ /book/) ||
(v[:item] =~ /pills/)
taxes<<0
else
subtotal = v[:price]
# v[:price]+=(((((10 * subtotal)/100)*20.ceil) / 20.0))
# taxes<<(10 * subtotal)/100
taxes<<((10 * subtotal/100)*20).ceil/20.0
end
end
taxes
end

def generate_receipt(receipt)
n = 0
while n < receipt.length
puts receipt["#{n+1}"][:price].inspect
receipt["#{n+1}"][:price]+=calculate_sales_tax(receipt)[n].round(2)
puts receipt["#{n+1}"][:price].inspect
puts calculate_import_tax(receipt)[n]
receipt["#{n+1}"][:price]+=calculate_import_tax(receipt)[n].round(2)
puts receipt["#{n+1}"][:price].inspect
puts "-----"
n+=1
end
receipt
end
Returns:
47.5
52.25
2.65
54.9
The bug is in your calculate_import_tax method. It's returning 2.65, not 2.40.
EDIT:
Got it :).
receipt["#{n+1}"][:price]+=calculate_sales_tax(receipt)[n]
receipt["#{n+1}"][:price]+=calculate_import_tax(receipt)[n]
Those rows are updating the price of the receipt. Hence, your tests are running independently, but the sales tax is modifying the raw price before the import tax is calculated...

1) Ruby treat numbers without decimal part as INTEGERS. And WILL NOT create result that have decimal part unless computation include floating point number.
2) 10 * X / 100 * 20 == X * 2
3) LEGAL COUNTING may require specified accuracy, and so you may need to use specialized library for such number crunching.
Fast solution would be to change that code to:
10.0 * subtotal / 100.0 * 20.0
Now Ruby will treat all those numbers as float's.

Related

Ruby: How to return positive integer or 0 if number is negative? [duplicate]

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?
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.
Thinking we can extend the Numeric class?
class Numeric
def non_negative
self > 0 ? self : 0
end
end
class Calculator
def initialize(subtotal: subtotal, discount: discount)
#subtotal = subtotal
#discount = discount
end
def total
(#subtotal - #discount).non_negative
end
end
A plain if statement might be easier to understand:
def total
if discount > subtotal
0
else
subtotal - discount
end
end
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
Just to clarify more, we need to add classes to be extended in core_ext.rb . file :
1) Create core_ext.rb file under config\initializers folder in your project.
2) Paste below as mentioned by #songyy in his answer:
class Numeric
def non_negative
self > 0 ? self : 0
end
end
Reference:
https://guides.rubyonrails.org/plugins.html#extending-core-classes

Can this be optimized without using a global variable?

I've recently begun learning ruby and I'm trying to avoid using global variables where possible. I wrote the below program which accepts user input and outputs math tables of the users choice (currently just +, * but to be expanded upon). I'm following suggestions from https://adriann.github.io/programming_problems.html to get me learning.
class User_input
.
# multiply
def User_input.mult1_to_12
by = (0..12).each do | range |
result = $choice_int * range
puts "#{$choice_int} x #{range} = #{result}"
end
end
# add
def User_input.add1_to_12
add = (0..12).each do | range |
result = $choice_int + range
puts "#{$choice_int} + #{range} = #{result}"
end
end
# accepts user input
puts "Please enter the tables you require (1-12): "
$choice_int = gets.to_i
puts "You have selected #{$choice_int}"
puts "Which tables do you require (+ - * /): "
choice_method = gets.chomp
puts "the method you have chosen is #{choice_method}"
if choice_method == "*"
User_input.mult1_to_12
elsif
choice_method == "+"
add1_to_12
end
end
You will note that I am currently using a global variable for $choice. Can someone with more experience suggest a more optimal solution. Please feel free to tear my code apart : ) Thanks.
Methods can accept parameters, for example:
# add numbers
def add(a,b)
a+b
end
puts add(1,2)
# will output 3
Here's a simple modification to your code using parameters:
class UserInput
# multiply
def self.mult1_to_12(choice_int)
(0..12).each do | range |
result = choice_int * range
puts "#{choice_int} x #{range} = #{result}"
end
end
# add
def self.add1_to_12(choice_int)
(0..12).each do | range |
result = choice_int + range
puts "#{choice_int} + #{range} = #{result}"
end
end
end
# accepts user input
puts "Please enter the tables you require (1-12): "
choice_int = gets.to_i
puts "You have selected #{choice_int}"
puts "Which tables do you require (+ - * /): "
choice_method = gets.chomp
puts "the method you have chosen is #{choice_method}"
if choice_method == "*"
UserInput.mult1_to_12(choice_int)
elsif choice_method == "+"
UserInput.add1_to_12(choice_int)
end
And here's a bit prettier solution that can also handle - and / (and a bunch of other operations provided by Ruby's Fixnum):
class UserInputProcessor
# define accessors to instance variables
attr_accessor :choice_int, :choice_method
def process
(0..12).map do |range|
if choice_method.eql?('/')
next if range.eql?(0) # need to skip X/0 to avoid division by zero
range = range.to_f # need to convert range to float to get float results
end
"#{choice_int} #{choice_method} #{range.to_i} = #{choice_int.send(choice_method, range)}"
end
end
end
handler = UserInputProcessor.new
print "Please enter the tables you require (1-12): "
handler.choice_int = gets.chomp.to_i
puts "You have selected #{handler.choice_int}"
print "Which tables do you require (+ - * /): "
handler.choice_method = gets.chomp
puts "the method you have chosen is #{handler.choice_method}"
puts "And here are the results:"
puts handler.process.join("\n")

Are there more elegant ways to prevent negative numbers in Ruby?

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?
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.
Thinking we can extend the Numeric class?
class Numeric
def non_negative
self > 0 ? self : 0
end
end
class Calculator
def initialize(subtotal: subtotal, discount: discount)
#subtotal = subtotal
#discount = discount
end
def total
(#subtotal - #discount).non_negative
end
end
A plain if statement might be easier to understand:
def total
if discount > subtotal
0
else
subtotal - discount
end
end
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
Just to clarify more, we need to add classes to be extended in core_ext.rb . file :
1) Create core_ext.rb file under config\initializers folder in your project.
2) Paste below as mentioned by #songyy in his answer:
class Numeric
def non_negative
self > 0 ? self : 0
end
end
Reference:
https://guides.rubyonrails.org/plugins.html#extending-core-classes

Find percentage of two small numbers in ruby

I have two small numbers that I'd like to find the percentage of.
First number: 0.683789473684211
Second number: 0.678958333333333
I want to find out what percentage of the number is bigger or smaller. These happen to be small numbers, but they could be bigger. The first number COULD be 250, and the second number could be 0.3443435. What I'm TRYING to do is detect whether the first number is 25% bigger than the second number.
I tried using this:
class Numeric
def percent_of(n)
self.to_f / n.to_f * 100.0
end
end
But it kept saying I was dividing by zero
How would you do it?
Why not shoot straight for what you say you want to do?
class Numeric
def sufficiently_bigger?(n, proportion = 1.25)
self >= proportion * n
end
end
p 5.sufficiently_bigger? 4 # => true
p 5.sufficiently_bigger? 4.00001 # => false
This will default to a 25% larger check, but you can override the proportionality by supplying a different value as the second argument.
It's generally easier and avoids the need for an explicit zero-denominator check if you express ratios in product form rather than using division.
The basic implementation of your code looks correct to me. Can you provide the specific example and expected output that is producing that error?
Just because I was curious I took your code and executed it with a small test suite and had 3 passing tests.
require 'rubygems'
require 'test/unit'
class Numeric
def percent_of(n)
self.to_f / n.to_f * 100.00
end
end
class PercentageTeset < Test::Unit::TestCase
def test_25_is_50_percent_of_50
assert_equal (25.percent_of(50)), 50.0
end
def test_50_is_100_percent_of_50
assert_equal (50.percent_of(50)), 100.0
end
def test_75_is_150_percent_of_50
assert_equal (75.percent_of(50)), 150.0
end
end
class Numeric
def percent_of(n)
self.to_f / n.to_f * 100.0
end
end
p 0.683789473684211.percent_of(0.678958333333333)
--output:--
100.71155181602376
p 250.percent_of(0.3443435)
--output:--
72601.9222084924
p 0.000_001.percent_of(0.000_000_5)
--output:--
200.0
p 0.000_000_000_01.percent_of(0.000_000_000_01)
--output:--
100.0
class Numeric
def percent_of(n)
self.to_f / n.to_f * 100.0
end
end
numbers = [ 0.683789473684211, 0.678958333333333 ]
min_max = {min: numbers.min, max: numbers.max}
puts "%<min>f is #{min_max[:min].percent_of(min_max[:max])} of %<max>f" % min_max
This program has opinions in that it shows what percentage the minimal number is of the maximal number, and shows the numbers.
If you use %d for the String#format method, you will show 0's. Perhaps that was what you were referring to, not sure.
Edit: Using minmax as suggested.
class Numeric
def percent_of(n)
self.to_f / n.to_f * 100.0
end
end
numbers = [ 0.683789473684211, 0.678958333333333 ]
min_max = Hash.new
min_max[:min], min_max[:max] = numbers.minmax
puts "%<min>f is #{min_max[:min].percent_of(min_max[:max])} of %<max>f" % min_max
I like the first version as the hash is built as it is needed, rather than initalized and then built.

Calculate Relative Strength of values in Ruby

I have a JSON with many records, for this question lets assume 100. There are two keys, Change and Sign. Change is something like 0.14 and Sign is one of two symbols "+" or "-".
For each one of the records, I would like to add two more keys "20 record up total" and "20 record down total". Where these equaled the sum of the last 20 records where the sign was "+" and "-" respectively.
So, for "20-record up total" something like:
array.collect {|array| array['change']}
array.collect {|array| array['sign']}
if sign = "+" then
#store change in uparray
#when uparray has 20 records add it up and create variable 20recorduptotal
Any help is appreciated. Thanks a lot
I believe I need to use .each_cons(20) but of only the records with the applicable sign.
Update: here's something that might actually work :)
I've defined a "summing queue" that calculates the sum of the last 20 items added to it. Its sum returns nil if it has fewer than 20 items.
class SummingQueue
def initialize(length = 20)
#values = []
#sum = 0
#length = length
end
def <<(value)
#values << value
#sum += value
#sum -= #values.shift if #values.length > #length
end
def sum
return nil if #values.length < #length
#sum
end
end
queues = Hash.new {|hash, key| hash[key] = SummingQueue.new }
array.each do |item|
queues[item["sign"]] << item["change"]
item["last_20_ups"] = queues["+"].sum
item["last_20_downs"] = queues["-"].sum
end
Note that the last_20_ups will include the current value (if applicable) - if you want it to be the last 20 values not including the current one, then move the queues[...] << item[...] line to the end of the each block.

Resources