Smooth
Smooth

Reputation: 956

Having trouble with a Ruby rounding error

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

Upvotes: 0

Views: 168

Answers (2)

Joe Pym
Joe Pym

Reputation: 1836

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...

Upvotes: 1

przemo_li
przemo_li

Reputation: 4053

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.

Upvotes: 0

Related Questions