How to display output with two digits of precision - ruby

Here is my code
class Atm
attr_accessor :amount, :rem, :balance
TAX = 0.50
def transaction
#rem = #balance=2000.00
#amount = gets.chomp.to_f
if #amount%5 != 0 || #balance < #amount
"Incorrect Withdrawal Amount(not multiple of 5) or you don't have enough balance"
else
#rem = #balance-(#amount+TAX)
"Successful Transaction"
end
end
end
a=Atm.new
puts "Enter amount for transaction"
puts a.transaction
puts "Your balance is #{a.rem.to_f}"
and my output is
Enter amount for transaction
100 # user enters this value
Successful Transaction
Your balance is 1899.5
as you can see the output, 'Your balance is 1899.5' only displays one digit of precision. I need help to understand and fix the issue. I want two digits of precision in the output.
And also how can I improve this code?

You can use this:
puts "Your balance is #{'%.02f' % a.rem}"
But remember that this code will round your result if you have more than 2 decimal places. Ex.: 199.789 will become 199.79.

It's a fundamental design flaw to store money as a floating point number because floats are inexact. Money should always be stored as an integer in the smallest unit of currency.
Imagine two accounts with 1.005. Display them both, and suddenly there is an extra penny in the world.
Instead store the amount of money in an integer. For example, $1 would be balance = 100 or 100 pennies. Then format the displayed value:
money = 1000
"%.2f" % (money / 100.0)
# => 10.00

number_with_precision(value, :precision => 2)
Should work in Rails

Related

Float not equal after division followed by multiplication

I'm testing a small and simple library I made in Ruby. The goal is to convert from EUR to CNY and vice versa. Simple.
I tested it to be sure everything works but I got an unexpected issue. When I use to_euro followed by to_yuan it should go back to the original amount ; it doesn't happen. I tried to .to_f or round(2) the amount variable which fix some tests, raise new ones, but it's never equal to what I expect globally ; I'm running out of idea to fix this :(
class Currency
attr_reader :amount, :currency
def initialize(amount, currency='EUR')
#amount = amount
#currency = currency
end
def to_yuan
update_currency!('CNY', amount * Settings.instance.exchange_rate_to_yuan)
end
def to_euro
update_currency!('EUR', amount / Settings.instance.exchange_rate_to_yuan)
end
def display
"%.2f #{current_symbol}" % amount
end
private
def current_symbol
if currency == 'EUR'
symbol = Settings.instance.supplier_currency.symbol
elsif currency == 'CNY'
symbol = Settings.instance.platform_currency.symbol
end
end
def update_currency!(new_currency, new_amount)
unless new_currency == currency
#currency = new_currency
#amount = new_amount
end
self
end
end
Tests
describe Currency do
let(:rate) { Settings.instance.exchange_rate_to_yuan.to_f }
context "#to_yuan" do
it "should return Currency object" do
expect(Currency.new(20).to_yuan).to be_a(Currency)
end
it "should convert to yuan" do
expect(Currency.new(20).to_yuan.amount).to eql(20.00 * rate)
end
it "should convert to euro and back to yuan" do
# state data test
currency = Currency.new(150, 'CNY')
expect(currency.to_euro).to be_a(Currency)
expect(currency.to_yuan).to be_a(Currency)
expect(currency.amount).to eql(150.00)
end
end
context "#to_euro" do
it "should convert to euro" do
expect(Currency.new(150, 'CNY').to_euro.amount).to eql(150 / rate)
end
end
context "#display" do
it "should display euros" do
expect(Currency.new(10, 'EUR').display).to eql("10.00 €")
end
it "should display yuan" do
expect(Currency.new(60.50, 'CNY').display).to eql("60.50 ¥")
end
end
end
And here's my RSpec result
I'm pretty sure this problem is very common, any idea how to solve it easily ?
Float isn't an exact number representation, as stated in the ruby docs:
Float objects represent inexact real numbers using the native architecture's double-precision floating point representation.
This not ruby fault, as floats can only be represented by a fixed number of bytes and therefor cannot store decimal numbers correctly.
Alternatively, you can use ruby Rational or BigDecimal
Its is also fairly common to use the money gem when dealing with currency and money conversion.

I keep getting this error in rubyTest.rb:11:in `+': String can't be coerced into Fixnum (TypeError) from Test.rb:11:in `<main>'

Im trying to do a project for school and i can't figure out how to fix this error. The equations I'm using look correct. I can't find the mistake. Are the equations right?
puts "Hello welcome to the bank of homeless people! How may I help you?"
puts "--type 'deposit' to deposit money to your checkings account"
puts "--type 'withdraw' to withdraw money from your checking account"
puts "--type 'display' to display your currant balance"
descion = gets.chomp.downcase
balance=0
case descion
when 'deposit'
puts "how much do you want to deposit?"
deposit = gets.chomp
balance = balance + deposit
puts "#{deposit} has been added to your checking account"
when 'withdraw'
puts "how much do you want to remove?"
withdraw = gets.chomp
balance = balance - withdraw
puts "#{withdraw} has been removed from your checking account"
when 'display'
puts "you have #{balance} in your account"
end
gets returns a string so your equations are trying to add a fixnum with a string.
Try using to_i on deposit and withdraw
A string is not a digit, therefore Ruby is attempting to find a digit but can't find it within your string.
What you need to do is convert the strings into digits..
For example:
x = "1_000"; #<= "1000" normal string
y = "1_000".to_i; #<= 1000 string converted to integer
You can also go one step further and call .to_f which will turn it into a floating point number, or a decimal.
z = "1_000.to_f; #<= 1000.0 string converted to floating point num
I'd suggest you go and look around on the web for some information on strings and how to use them, along with what you can do with them. Here's a good start: http://ruby-doc.org/core-2.0.0/String.html

Having a method update/reload its self everytime it gets called? Ruby

My long term goal is replicate this spreadsheet in a ruby program and then to a rails app.
Currently I am trying to make it determine which of the two debts has the highest interest then subtract the minimum amount from that debt as well as any extra amounts the person is willing to pay as well as subtracting the minimum value from the other debt.
example:
card = balance: $10,000, Minimum: $200, i% = 20%
loan = balance: $40,000, Minimum: $400, i% = 5%
payments made per month = $1000
In this case, the program would every month firstly take $600 ($1000 -($200 + $400) + $200) from the card until it's balance was 0 then take $1000 ($1000 - $400 + $400) until the loan was payed off and return how many months that would take.
Currently, I am trying to get the amount to subtract each month take into account the balance of the debt and have this update whenever the method is called - however this does not seem to be working and will stay at $400 for both debts (snowball_amount method). EDIT: missing method issue fixed. Needed to change attr_reader to attr_accessorAlso for some reason, when I pass a debt object into highest_interest, i'm getting an undefined method 'balance=' error. Would be grateful for some help with this!
Create a Debt Class
class Debt
def initialize(balance: b, monthly_payment: m, annual_interest_rate: a)
#balance = balance
#monthly_min = monthly_payment
#int_rate = annual_interest_rate
end
attr_reader :monthly_min, :balance, :int_rate
end
Create two debt objects
#debt1 = Debt.new(balance: 14000.0, monthly_payment: 200.0, annual_interest_rate: 0.06)
#debt2 = Debt.new(balance: 40000.0, monthly_payment: 400.0, annual_interest_rate: 0.08)
Put them into array
#debts_array = [#debt1, #debt2]
Set the amount the person is willing to pay each month
#payment = 1000.0
Determine how much extra is being payed i.e. #payment - each debts monthly minimum, only if that debt's balance is over 0
def snowball_amount
#payments_less_mins = #payment
#debts_array.each do |debt|
if debt.balance <= 0
#payments_less_mins
elsif debt.balance > 0
#payments_less_mins = #payments_less_mins - debt.monthly_min
end
end
puts #payments_less_mins
return #payments_less_mins
end
Method for calculating the balance of that debt for that month
def compounding_interest(balance, apr)
return balance * (1 + apr/12)**1
end
Determing how long it will take to pay the debt off. While the balance of the debt is above 0 firstly update the balance in line with the addition of interest, then subtract from the debt balance the minimum monthly payment and the snowball(extra amount) from the balance. Then set the debts balance to 0
def highest_interest(debt, snowball)
months_to_pay = 0
while debt.balance > 0
debt.balance = compounding_interest(debt.balance, debt.int_rate)
debt.balance = debt.balance - (debt.monthly_min + snowball)
months_to_pay += 1
end
debt.balance = 0
snowball_amount
puts months_to_pay
end
Determine which debt has the highest balance and then do the highest interest method on that debt.
def which_has_higher_interest
debts_array = #debts_array.sort{|i| i.int_rate}.reverse!
puts debts_array[0].balance
debts_array.each do |debt|
highest_interest(debt, snowball_amount)
end
end
Calling the which_has_higher_interest method
puts which_has_higher_interest
In lines 3, 4, and 7 of your highest_interest method, you are calling a method called balance= on a Debt object, but your Debt objects do not have such a method. You need to define it somehow, possibly by changing the line
attr_reader :monthly_min, :balance, :int_rate
to
attr_reader :monthly_min, :int_rate
attr_accessor :balance

Multiplying variables in Ruby?

I'm trying to create a simple pay predictor program.
I am capturing two variables, then want to multiply those variables. I have tried doing
pay = payrate * hourrate
but it seems not to work. I was told to put pay in a definition, then I tried to display the variable but it still didn't work. When I looked at the documentation, it seems that I am using the correct operator. Any guesses?
# Simple Budgeting Program
puts "What is your budget type?\nYou can say 'Monthly' 'Weekly' or 'Fortnightly'"
payperiod = gets.chomp
case payperiod
when "Monthly"
puts "You are paid monthly"
when "Weekly"
puts "You are paid weekly"
when "Fortnightly"
puts "You are paid every two weeks."
else
puts "You did not input a correct answer."
end
puts "What is your hourly rate?\n"
payrate = gets.chomp
puts "How many hours have you worked during your pay period?\n"
hourrate = gets.chomp
def pay
payrate * hourrate
end
puts "#{pay}"
preventclose = gets.chomp
The def has nothing to do with it. payrate and hourrate are strings and * means a very different thing to strings. You need to convert them to numbers first with to_i or to_f.
payrate.to_f * hourrate.to_f
You declared pay rate and hour rate as strings. In Ruby, you cannot multiply strings by other strings. However, in Ruby there are type conversions. Ruby's string class offers a method of converting strings to integers.
string = "4"
string.to_i => 4
In your case, you first need to convert BOTH strings to an integer.
def pay
payrate.to_i * hourrate.to_i
end
Here's some great information about strings.
http://www.ruby-doc.org/core-2.1.2/String.html
Hope this helps
Coercing Strings into Numbers, and Back Again
Kernel#gets generally returns a string, not an integer or floating point number. In order to perform numeric calculations, you need to coerce the string into a number first. There are a number of ways to do this, but the Kernel#Float method is often safer than String#to_i because the Kernel method will raise an exception if a string can't be coerced. For example:
print 'Pay Rate: '
rate = Float(gets.chomp)
print 'Hours Worked: '
print hours = Float(gets.chomp)
Of course, operations on floating point numbers can be inaccurate, so you might want to consider using Kernel#Rational and then converting to floating point for your output. For example, consider:
# Return a float with two decimal places as a string.
def pay rate, hours
sprintf '%.2f', Rational(rate) * Rational(hours)
end
p pay 10, 39.33
"393.30"
#=> "393.30"

I can't multiply decimals, in ruby

I'm making a interest calculator
I go 10 * .10 * 10 and i get 0 so how do i multiply a decimal without it being 0?
my source code is
def interest()
puts "Type the original loan."
loan = gets.chomp.to_i
puts "Type the amount of interest in decimal."
interest = gets.chomp.to_i
puts "How many years?"
years = gets.chomp.to_i
puts "Your interest is"
puts loan * interest * years
end
interest()
You've got integers there, so result will be an integer too. You could use 'to_f but beware, it's not good for dealing with money or anything else needing precision. Use BigDecimal instead:
require 'bigdecimal'
def interest
puts "Type the original loan."
loan = BigDecimal(gets.chomp)
puts "Type the amount of interest in decimal."
interest = BigDecimal(gets.chomp)
puts "How many years?"
years = BigDecimal(gets.chomp) # suggested in comment, agreed with that
puts "Your interest is"
puts loan * interest * years
end
What's the difference between them?
Do this
interest = gets.chomp.to_f
.to_i changes the string to an integer. An integer is a WHOLE number.
.to_f is to float, a float is a number that allows decimal places
The problem is that you're using .to_i when you don't really want to use integers here. Integers are represented without decimal parts to them, and therefore when you call .10.to_i it truncates it to 0.
Consider using floats by .to_f instead

Resources