Truncate a floating point number without rounding up
Asked Answered
L

9

30

I have a floating point number that I want to truncate to 3 places but I don't want to round up.

For example, convert 1.0155555555555555 to 1.015 (not 1.016).

How would I go about doing this in Ruby?

Lyndy answered 12/11, 2011 at 18:43 Comment(2)
What should happen for negative numbers, e.g. -1.01555 ?Vivien
@MarkByers For what I am doing, the number will never be negative.Lyndy
V
22

Assuming you have a float, try this:

(x * 1000).floor / 1000.0

Result:

1.015

See it working online: ideone

Vivien answered 12/11, 2011 at 18:47 Comment(2)
Not always, for example (128.653 * 1000) / 1000.0 => 128.652 # doesn't work - because the multiplied value comes out as 128652.999999Liquescent
This is not an answer because it relies on floor rounding, which makes no sense.Garpike
P
25

You can also convert to a BigDecimal, and call truncate on it.

1.237.to_d.truncate(2).to_f # will return 1.23
Poleaxe answered 29/10, 2015 at 20:41 Comment(6)
Thanks, Sid. That's exactly the approach I was looking for.Gills
Cheers @ElliotLarson!Poleaxe
It fails on 59.99999999999999999999.to_d.truncate(2).to_f, returns 60.0!!Lewak
@Lewak That's because precision is too fine for a regular Float. Even 59.99999999999999999999.inspect is "60.0". So the conversion happens prior to to_d.Unfleshly
Hey guys, there's a precision glitch when integer portion is big enough. 111111111.9999999.to_d.truncate(2).to_f is 111111112.0. See my hacked version below. Cheers!Unfleshly
@Lewak I would recommend reading: floating point arithmetic for computer processors: en.wikipedia.org/wiki/Floating-point_arithmeticGarpike
V
22

Assuming you have a float, try this:

(x * 1000).floor / 1000.0

Result:

1.015

See it working online: ideone

Vivien answered 12/11, 2011 at 18:47 Comment(2)
Not always, for example (128.653 * 1000) / 1000.0 => 128.652 # doesn't work - because the multiplied value comes out as 128652.999999Liquescent
This is not an answer because it relies on floor rounding, which makes no sense.Garpike
S
19

Since ruby 2.4 Float#truncate method takes as an optional argument a number of decimal digits:

1.0155555555555555.truncate(3)
# => 1.015
Sensitize answered 31/7, 2017 at 10:16 Comment(0)
C
7

Multiply by a thousand, floor, divide by a thousand, making sure to do a float division.

(x * 1000).floor / 1000.0

Or, in Ruby 1.9.2, using a version of round that wasn't available in earlier versions,

(x - 0.0005).round(3)
Caller answered 12/11, 2011 at 18:49 Comment(1)
Don't you just love doubles... pry(main)> (1.0155555 * 1000).floor / 1000.0 => 1.015 # works fine pry(main)> (128.653 * 1000).floor / 1000.0 => 128.652 # doesn't work - because the multiplied value comes out as 128652.999999Liquescent
A
3

sid's answer is fine but it misses the first requirement and thus fails Anwar's test. the requirement there is we must start raw so that ruby does not convert the number readily. and to start raw as raw gets is to use a plain string, so

> "59.99999999999999999999".to_d.truncate(2)
=> #BigDecimal:55a38a23cd68,'0.5999E2',18(45)>
> "59.99999999999999999999".to_d.truncate(2).to_s
=> "59.99"
> "59.99999999999999999999".to_d.truncate(2).to_f
=> 59.99

just sharing this now, since i just encountered this problem myself today : )

Allegiance answered 12/10, 2016 at 4:28 Comment(0)
U
1

This solution is based on a brilliant BigDecimal trick by @SidKrishnan, but can also handle bigger floats without faltering on precision issues.

# Truncate a floating-point value without rounding up.
#
#   trunc_float(1.999, 2)   # => 1.99
#   trunc_float(1.999, 0)   # => 1.0
#
# @param value [Float]
# @param precision [Integer]
# @return [Float]
def trunc_float(value, precision)
  BigDecimal(value.to_s).truncate(precision).to_f
end

#--------------------------------------- Test

describe ".trunc_float" do
  def call(*args)
    trunc_float(*args)
  end

  it "generally works" do
    [
      [[1, 0], 1.0],
      [[1.999, 4], 1.999],
      [[1.999, 3], 1.999],
      [[1.999, 2], 1.99],
      [[1.999, 1], 1.9],
      [[1.999, 0], 1.0],
      [[111111111.9999999, 3], 111111111.999],
      [[1508675846.650976, 6], 1508675846.650976],
    ].each do |input, expected|
      output = call(*input)
      expect([input, output]).to eq [input, expected]
    end
  end
end
Unfleshly answered 23/10, 2017 at 12:34 Comment(0)
G
0

I saw the more 'computational' way of doing this is not within the answers. You can consider using the method below.

This will also work in other programming languages, like C/ Java/ Python etc. (however the casting syntax would be different).

q = 1.0155555555555555
(q * 1000).to_i / 1000.0
=> 1.015
Garpike answered 30/7, 2019 at 10:48 Comment(0)
D
0

You have few options - some of them are mentioned in the answers above, but not all.

You can use .floor(n)

# Returns the largest number less than or equal to
# float with a precision of n digits decimal digits (default: 0).
# -> basically its rounding down the number

> 1.0155555555555555.floor(3)
1.015

You can use .truncate(n)

# Returns float truncated to a precision 
# of n digits decimal digits (default: 0).

> 1.0155555555555555.truncate(3)
=> 1.015

You can use .round(n, :truncate) (Only on BigDecimal not on float)

> 1.0155555555555555.to_d.round(3, :truncate).to_f
=> 1.015
Dallman answered 26/2 at 13:32 Comment(0)
T
-1

You can do this using regex, since ruby/rails have precision limit.

-- first convert the number to string and then do following -

input = "114.99999999999999999999"

input[/\d+.\d/]

114.99

Toledo answered 29/12, 2017 at 11:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.