570 words
3 minutes

JavaScript toFixed Returns a String: Fix Addition Before Formatting

2026-10-25
Frontend
JavaScript
/
Node.js
/
Troubleshooting
Contents

(1.25).toFixed(2) + (2.5).toFixed(2) produces '1.252.50'. Each formatted value is a string, so + joins them. Keep values numeric while calculating, then call toFixed(2) on the final result when you need display text.

Converting the formatted string back to a number can restore arithmetic, but it also discards trailing zeros and preserves any rounding already applied. The regression fixture below distinguishes those three decisions.

Put formatting after the calculation#

Number.prototype.toFixed returns text. For these inputs, the calculation should be (1.25 + 2.5).toFixed(2), which returns '3.75'. Store that result in a display field rather than overwriting the numeric inputs.

If your inputs arrive as strings, validate and convert them before calculating. Do not apply parseInt to formatted decimal amounts: it discards the fractional part. The separate integer conversion guide explains why prefix parsing and complete numeric conversion have different acceptance rules.

Required resultOperationConsequence
Numeric suma + b with numeric operandsThe result remains available for arithmetic.
Two-place display text(a + b).toFixed(2)The result is a string.
Number from existing formatted textNumber(text) after validating the input contractTrailing zeros are not retained as display metadata.
Total of individually rounded itemsConvert each formatted item, then sumCan differ from rounding the original total.

The last row is a business rule, not an interchangeable implementation of the second row. Decide where rounding belongs before changing the pipeline.

Run a regression fixture#

Save this as tofixed-check.mjs and run node tofixed-check.mjs. These assertions were executed during article preparation on macOS with Node.js 24.13.0. They use finite numeric literals and built-in formatting; they do not implement a currency accounting policy.

import assert from 'node:assert/strict';
const a = 1.25;
const b = 2.5;
assert.equal(typeof a.toFixed(2), 'string');
assert.equal(a.toFixed(2) + b.toFixed(2), '1.252.50');
assert.equal((a + b).toFixed(2), '3.75');
assert.equal(Number(a.toFixed(2)) + Number(b.toFixed(2)), 3.75);
console.log('addition: concatenation reproduced; numeric sum passed');
const display = (2.5).toFixed(2);
assert.equal(display, '2.50');
assert.equal(Number(display), 2.5);
assert.equal(String(Number(display)), '2.5');
console.log('display: string keeps trailing zero; number does not');
const items = [0.125, 0.125];
const roundedItemsTotal = items.reduce(
(sum, item) => sum + Number(item.toFixed(2)), 0,
);
const originalTotal = items.reduce((sum, item) => sum + item, 0);
assert.equal(roundedItemsTotal.toFixed(2), '0.26');
assert.equal(originalTotal.toFixed(2), '0.25');
console.log('rounding: per-item=0.26; final-total=0.25');
assert.equal((1.005).toFixed(2), '1.00');
assert.equal((1e21).toFixed(2), '1e+21');
assert.equal((Infinity).toFixed(2), 'Infinity');
assert.throws(() => (1).toFixed(101), RangeError);
console.log('boundaries: binary rounding, large values, infinity, digits passed');

Expected output in that environment:

addition: concatenation reproduced; numeric sum passed
display: string keeps trailing zero; number does not
rounding: per-item=0.26; final-total=0.25
boundaries: binary rounding, large values, infinity, digits passed

The 0.125 inputs make the rounding-stage difference visible without relying on an approximate representation of that input. Rounding each item produces two 0.13 values; summing the original items produces 0.25. Converting the rounded strings back cannot recover the discarded precision.

Keep the boundary explicit#

The ECMAScript algorithm formats the Number value already stored. It does not recover an exact decimal value from the original source text. The 1.005 assertion demonstrates why a formatting method alone is insufficient to promise a particular decimal rounding policy.

For absolute values at least 1e21, formatting can use exponential notation. Non-finite inputs can produce text such as 'Infinity'; asking for two places does not validate the input. Validate finiteness separately when your application requires it. The supported digits range is 0 through 100; the fixture checks rejection above that range.

Browser engines, locale formatting, external form input, and a decimal arithmetic library were not tested here. For accounting rules, define the unit, rounding stage, and tie rule separately and verify the chosen implementation against those requirements.

Keep the arithmetic result and display string separate. The regression should prove both that addition stays numeric and that rounding happens at the stage your application intends.

References:

JavaScript toFixed Returns a String: Fix Addition Before Formatting
https://laplusda.com/en/posts/javascript-tofixed-string-addition/
Author
Zero
Published at
2026-10-25
License
CC BY-NC-SA 4.0