-3

I have an Item class, and I initialized five variables. I am trying to match the output in my terminal to the value of the expected_summary. I am calling Item.summary in the following code:

class Item
  attr_reader :name, :description, :manufacturer, :price, :summary

  def initialize (name, manufacturer, price, description=nil, summary=nil)
    @name = name
    @manufacturer = manufacturer
    @price = price
    @description = description
    if description
      @summary = "Name: #{name}
Description: #{description}
Manufacturer: #{manufacturer}
Price: $#{price}"
    else
      @summary = "Name: #{name}
Manufacturer: #{manufacturer}
Price: $#{price}"
    end
  end
end

#expected_summary = %q(Name: Spy Notebook
#Manufacturer: Spys-R-Us
#Price: $10.50)
item = Item.new("Spy Notebook", "Spys-R-Us", 10.50)
puts item.summary

When I pass a number 10.50 as the price argument, it returns as 10.5. I cannot figure out why. Why does Ruby read 10.50 as 10.5? Is there a way to correct this?

sawa
  • 165,429
  • 45
  • 277
  • 381
Jon
  • 13
  • 1
  • 3
    This has already been answered https://stackoverflow.com/questions/32542300/how-to-output-trailing-zero-in-ruby – Jay Mason Dec 12 '18 at 20:41
  • 4
    uh, 10.50 is the same as 10.5? – Pika Supports Ukraine Dec 12 '18 at 20:41
  • 3
    What's the difference between `10.50` and `10.5`? That's right, there is no difference! It's just the same value written in two ways. – axiac Dec 12 '18 at 20:48
  • I see that but I'm trying to match the expected_value exactly. So I want my output to be 10.50 and not 10.5. – Jon Dec 12 '18 at 20:56
  • 1
    Floating point numbers can often mismatch a tiny bit which is why test frameworks often have a `assert_approx_equal` type method to handle the slight differences. You may get `10.4999999999999361` as the result of a calculation, but really that's `10.50` rounded to two places. Does `.round(2)` do the job well enough for your case? Internally there is no `10.50`, it's stored instead much more like `1.050000000... * 10^1` typically using the [IEEE floating-point format](https://en.wikipedia.org/wiki/IEEE_754). – tadman Dec 12 '18 at 23:56

3 Answers3

1

The answer is in the string format operator which allows you to coerce the float into a string with 2 decimal places. This method will also work to do the rounding of 3 digit numbers, I didn't try it any further, but I'm fairly certain it will work. Here is your original code modified to showcase exactly how it would work.

class Item
  attr_reader :name, :description, :manufacturer, :price, :summary 

  def initialize (name, manufacturer, price, description=nil, summary=nil) 
    @name = name
    @manufacturer = manufacturer
    @price = "%.2f" % price
    @description = description

    if description
      @summary = "Name: #{name}
                  Description: #{description}
                  Manufacturer: #{manufacturer}
                  Price: $#{@price}"
    else
      @summary = "Name: #{name}
                  Manufacturer: #{manufacturer}
                  Price: $#{@price}"
    end
  end

  def price
     @price
  end
end

EDIT: I didn't see @tadman's comment until after posting this, he beat me to the answer.

David J. Davis
  • 906
  • 7
  • 20
0

Maybe you can define your custom method to format the number as string with two decimals:

def two_decimals(number)
  number = Float(number)
  price_split = number.to_s.split('.')
  price_split[1] = price_split.last + '0'*(2-price_split.last.size) if price_split.last.size < 2
  price_split.join('.')
end

two_decimals(10.25) #=> "10.25"
two_decimals(10.2) #=> "10.20"
two_decimals(10) #=> "10.00"

Or something better...

iGian
  • 11,023
  • 3
  • 21
  • 36
0

The usual way of formatting numbers is:

'%.02f' % number

Where that is the printf-style notation for describing how you want something formatted. This is inherited from C and shows up in a lot of other languages:

'%f' % number    # A floating-point number with default precision
'%.02f' % number # A floating-point number rounded to 2 places

Within Rails you also have helper methods like number_with_precision which can handle localization cases where the decimal separator is not a dot:

number_with_precision(number, precision: 2, locale: :fr)
# => 1,50
tadman
  • 208,517
  • 23
  • 234
  • 262