Search code examples
rubyroundingrounding-error

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

Solution

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