Skip to content

Fix Odoo Decimal Precision and Rounding Errors in Amounts and Prices

DeployMonkey Team · March 23, 2026 10 min read

The Problem

Your invoice total is off by $0.01. A tax calculation shows $13.49 instead of $13.50. A unit price of $9.99 displays as $10.00. Or a stock move fails with Rounding error: 0.000000001 is not zero. These decimal precision issues cause real business problems — accounting discrepancies, failed bank reconciliations, and unhappy customers.

Common Symptoms

  • Invoice totals off by 1 cent
  • Tax amounts not matching expected values
  • Quantity comparisons failing (10.0 != 10.000000001)
  • Price unit showing wrong decimal places
  • Stock moves stuck due to rounding validation
  • Multi-currency conversions producing wrong amounts

How Odoo Handles Decimal Precision

Odoo uses the decimal.precision model to define rounding rules for different use cases:

# Built-in precision categories:
# 'Product Price'  -> typically 2 decimal places
# 'Discount'       -> typically 2 decimal places  
# 'Product Unit of Measure' -> typically 3 decimal places
# 'Account'        -> follows currency rounding

# Defined in code as:
price = fields.Float(digits='Product Price')  # uses decimal.precision
amount = fields.Monetary(currency_field='currency_id')  # uses currency rounding

Root Causes and Fixes

1. Wrong Decimal Precision Setting

The most common cause. The default precision for "Product Price" is 2 decimal places, but your business needs 4 (e.g., commodity pricing).

Fix: Go to Settings > Technical > Database Structure > Decimal Accuracy. Find the relevant category and change the number of digits:

  • Product Price: increase to 4 for commodity pricing
  • Product Unit of Measure: increase to 5 for high-precision quantities
  • Discount: increase to 4 for fractional discounts

After changing, restart Odoo and update affected modules.

2. Floating-Point Comparison Bugs

Python floating-point arithmetic is inherently imprecise. Never compare floats with ==:

# BAD: floating-point comparison
if order.amount_total == expected_total:  # may fail!
    confirm_order()

# GOOD: use Odoo's float_compare
from odoo.tools import float_compare
if float_compare(order.amount_total, expected_total, precision_digits=2) == 0:
    confirm_order()

# float_compare returns:
#  -1 if a < b
#   0 if a == b (within precision)
#   1 if a > b

3. Tax Rounding Method

Odoo supports two tax rounding methods that give different results:

  • Round per Line: tax is rounded on each invoice line, then summed
  • Round Globally: taxes are summed first, then rounded once

These can produce different totals. If your totals do not match your tax authority's expectations, switch the method.

Fix: Settings > Accounting > Taxes > Tax Rounding Method. Change between "Round per Line" and "Round Globally".

4. Currency Rounding Mismatch

Each currency in Odoo has a rounding precision. If it is wrong, all monetary calculations for that currency will be off.

Fix: Go to Accounting > Configuration > Currencies. Check the Rounding Factor:

  • USD, EUR, GBP: 0.01 (2 decimal places)
  • JPY, KRW: 1 (no decimal places)
  • BHD, KWD: 0.001 (3 decimal places)

5. Stored Computed Field Rounding

A computed field that stores a rounded value can accumulate errors when summed:

# BAD: rounding each line then summing
@api.depends('line_ids.price_unit', 'line_ids.quantity')
def _compute_total(self):
    for rec in self:
        rec.total = sum(
            round(line.price_unit * line.quantity, 2) 
            for line in rec.line_ids
        )

# BETTER: sum first, round once
@api.depends('line_ids.price_unit', 'line_ids.quantity')
def _compute_total(self):
    for rec in self:
        raw_total = sum(
            line.price_unit * line.quantity 
            for line in rec.line_ids
        )
        rec.total = rec.currency_id.round(raw_total)

6. Import Data Rounding

When importing data from CSV or Excel, values may have been pre-rounded differently than Odoo expects.

Fix: Ensure imported amounts use full precision. Let Odoo handle rounding according to its precision settings.

Debugging Rounding Issues

# Check decimal precision settings:
SELECT name, digits FROM decimal_precision;

# Check currency rounding:
SELECT name, rounding FROM res_currency WHERE active = true;

# In Odoo shell, test rounding:
from odoo.tools import float_round, float_compare
float_round(10.555, precision_digits=2)  # 10.56
float_compare(10.00, 10.004, precision_digits=2)  # 0 (equal)

# Check a specific computation:
invoice = env['account.move'].browse(123)
for line in invoice.invoice_line_ids:
    print(f"{line.name}: qty={line.quantity} price={line.price_unit} "
          f"subtotal={line.price_subtotal} tax={line.price_total - line.price_subtotal}")

Best Practices

  • Always use float_compare() and float_round() from odoo.tools
  • Use fields.Monetary for money amounts — it respects currency rounding
  • Use fields.Float(digits='Category') for non-monetary decimals
  • Set decimal precision before importing data
  • Test with edge cases: $0.005 tax on 3 items, 1/3 quantities, multi-currency
  • Use currency_id.round() for monetary rounding in code