/commands/ruby/round-float-two-decimals
ruby round a float to 2 decimal places (round, format, BigDecimal)
- tool
- Ruby
- versions
- Ruby 2.4+ (half: option), Ruby 3.4
- os
- any
3.14159.round(2) rounds a Float to two decimal places and returns a Float (3.14). To display a number with exactly two decimals, including trailing zeros, use format("%.2f", x), which returns a String such as "5.00". For money and other values where exact decimal arithmetic matters, use BigDecimal or integer cents instead of Float.
Rounding vs formatting
The example runs CRuby 3.4 compiled to WebAssembly (ruby.wasm, the official Ruby project build) with Node.js:
npm init -y >/dev/null
npm install --silent --no-audit --no-fund @ruby/3.4-wasm-wasi@2.8.1 @ruby/wasm-wasi@2.8.1 >/dev/null 2>&1
cat > ruby.mjs <<'EOF'
import fs from "node:fs/promises";
import { DefaultRubyVM } from "@ruby/wasm-wasi/dist/node";
const wasm = await WebAssembly.compile(await fs.readFile("node_modules/@ruby/3.4-wasm-wasi/dist/ruby+stdlib.wasm"));
const { vm } = await DefaultRubyVM(wasm);
vm.eval(await fs.readFile(process.argv[2], "utf8") + "\n$stdout.flush");
EOF
cat > round.rb <<'EOF'
p 3.14159.round(2)
p 5.0.round(2)
p format("%.2f", 5.0)
p "%.2f" % 1234.5
p 2.5.round, 3.5.round
p 2.5.round(half: :even), 2.5.round(half: :down)
p 2.675.round(2)
p 3.149.floor(2), 3.141.ceil(2)
p 1234.5678.round(-2)
p (0.1 + 0.2)
p (0.1 + 0.2).round(2)
[-> { "3.14159".round(2) }, -> { 3.14159.round("2") }, -> { 3.14159.round(2, :even) }].each do |f|
f.call
rescue => e
puts "#{e.class}: #{e.message}"
end
EOF
node --no-warnings ruby.mjs round.rbOutput
3.14 5.0 "5.00" "1234.50" 3 4 2 2 2.68 3.14 3.15 1200 0.30000000000000004 0.3 NoMethodError: undefined method 'round' for an instance of String TypeError: no implicit conversion of String into Integer ArgumentError: wrong number of arguments (given 2, expected 0..1)
round drops trailing zeros because it returns a number (5.0), while format returns a String with exactly the digits requested. By default .5 rounds away from zero (2.5.round is 3); half: :even (banker's rounding) and half: :down change that. The three error lines at the end come from the pitfalls listed below.
Methods
| Code | Returns | Notes |
|---|---|---|
| x.round(2) | Float | Round half away from zero by default |
| x.round(2, half: :even) | Float | Banker's rounding (Ruby 2.4+) |
| x.floor(2) / x.ceil(2) | Float | Always down or up |
| x.round(-2) | Integer | Round to hundreds |
| format("%.2f", x) or "%.2f" % x | String | Fixed two decimals for display, trailing zeros kept |
| x.truncate(2) | Float | Cut off the extra digits toward zero |
| BigDecimal("2.675").round(2) | BigDecimal | Exact decimal arithmetic for money |
Pitfalls
- Float representation.
0.1 + 0.2is0.30000000000000004; rounding hides it for display, but sums of many Floats still drift. Store money as integer cents orBigDecimal. - Rounding a String.
"3.14159".round(2)raisesNoMethodError(undefined methodroundfor an instance of String); convert withFloat()orto_ffirst. - Wrong arguments.
round("2")raisesTypeError(no implicit conversion of String into Integer), andround(2, :even)without thehalf:keyword raises wrong number of arguments (given 2, expected 0..1). - BigDecimal is a gem. Since Ruby 3.4,
bigdecimalis a bundled gem rather than a default gem:require "bigdecimal"works in a standard installation, but with Bundler it must be listed in theGemfile, and Ruby 3.4 prints a warning when it is missing. - puts vs p.
puts 5.0.round(2)prints5.0,pprints the same with quotes for Strings; see puts vs print vs p.
Confirm it worked
Print the value with p to see whether it is a Float (3.14) or a String ("3.14"), and test a value ending in 5 (2.675, 2.5) to check that the rounding mode is the one you want.
FAQ
Why does 2.675.round(2) give 2.68 in Ruby but 2.67 in Python?
Ruby's Float#round rounds the decimal number closest to the float, while Python's round works on the exact binary value, which is slightly below 2.675. Print the results in both languages before relying on either for money; use BigDecimal or integer cents.
How do I always show two decimals, such as 5.00?
round returns a Float, and 5.0 prints as 5.0. Use format("%.2f", x), which returns the String "5.00".
How do I round down or up instead?
Float#floor(2) and Float#ceil(2) accept a number of digits too: 3.149.floor(2) is 3.14 and 3.141.ceil(2) is 3.15.