Convert a number to a string in fixed-point format (toFixed)

When you need a string with a specific number of digits after the decimal point—no scientific notation, just plain fixed-point text—reach for Number.prototype.toFixed. It formats a number into a string with exactly the number of fractional digits you ask for, rounding if necessary and padding with zeros when there aren’t enough digits.

(12.345).toFixed(2);   // "12.35"
(12).toFixed(3);       // "12.000"

Syntax and return value

Call toFixed on a number and pass the number of digits you want after the decimal. The result is always a string. If you omit the argument, it defaults to 0.

const n = 1234.5;
n.toFixed();    // "1235"
n.toFixed(1);   // "1234.5"
typeof n.toFixed(1); // "string"

The allowed range for digits is 0 to 100 (inclusive). Passing a value outside that range throws RangeError.

Rounding rules, precisely

toFixed rounds to the nearest value at the requested precision. If the value is exactly halfway, it rounds up (away from zero, after the sign is separated), per the ECMAScript algorithm that selects the larger candidate in a tie. This is why 2.35 with one fractional digit becomes "2.4".

(2.34).toFixed(1);  // "2.3"
(2.35).toFixed(1);  // "2.4"  // tie rounds up
(-2.35).toFixed(1); // "-2.4" // still away from zero overall

You may see surprising results when binary floating-point cannot exactly represent your input. A classic example is 2.55, whose closest IEEE-754 representation is slightly less than 2.55, so it rounds down to "2.5" at one decimal place. This is not a bug in toFixed; it’s the nature of floating-point.

(2.55).toFixed(1);  // "2.5"   // representation is a hair under 2.55
(2.449999999999999999).toFixed(1); // "2.5" // same value as 2.45, so it rounds up

Range, errors, and edge cases

If digits is not an integer in the valid range, you’ll get a RangeError. If you invoke toFixed with a non-number this value, you’ll get a TypeError. Keep in mind that the method is not generic; it’s meant for Number values (or Number wrappers).

(123).toFixed(101);      // RangeError
Number.prototype.toFixed.call("123", 2); // TypeError

Large numbers, non-finite values, and when exponential notation appears

For extremely large magnitudes, toFixed defers to the same algorithm as Number.prototype.toString and can use exponential notation when the absolute value is at least 10^21. Non-finite values round-trip to string forms like "Infinity" or "NaN". Also, toFixed(0) can preserve more detail than toString() for some integers near the edges of precision.

(1.23e20).toFixed(2);     // "123000000000000000000.00"
(6.02e23).toFixed(50);    // "6.019999999999999e+23" // still exponential
(1000000000000000128).toString(); // "1000000000000000100"
(1000000000000000128).toFixed(0); // "1000000000000000128"

Negative numbers, -0, and string signs

toFixed handles the sign separately from magnitude. It pulls off the sign, rounds the absolute value, then concatenates the sign back. That detail matters because very small negative numbers can round to "0.00" with a minus sign, producing "-0.00"—a consequence of IEEE-754 signed zero and the spec’s sign-preserving steps. If you prefer to normalize it to "0.00", coerce the string after formatting.

(-0.0004).toFixed(2);           // "-0.00"
(-0.0004).toFixed(2).replace(/^-0\./, "0."); // "0.00"

One more practical gotcha: member access binds tighter than unary -. If you write -2.34.toFixed(1), the call happens on 2.34, and then the result is negated back to a number. Wrap negatives in parentheses when you want a string result.

-2.34.toFixed(1);   // -2.3   (a number)
(-2.34).toFixed(1); // "-2.3" (a string)

Using toFixed in real code

Use toFixed whenever you’re presenting numbers to a human and you need a fixed number of decimals. Remember that it returns a string; if you need to do math afterwards, convert it back to a number with Number(...), parseFloat(...), or the unary + operator.

const price = 19.9;
const label = price.toFixed(2);   // "19.90" — perfect for UI

// If you must compute with it again:
const numeric = Number(label);    // 19.9

If you need locale-aware separators (like , vs . or grouping), format with toLocaleString instead. Use toFixed for strict control of fractional digits, and use toLocaleString for human-friendly formatting in a specific locale.

(12345.6).toFixed(2);                 // "12345.60"
(12345.6).toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
// "12.345,60"

Converting inputs and guarding against surprises

Parse string inputs first, then fix:

function toMoney(value) {
  const n = Number.parseFloat(value);
  if (!Number.isFinite(n)) return "—";
  return n.toFixed(2);
}

toMoney("1.2");     // "1.20"
toMoney("1.2e3");   // "1200.00"
toMoney("abc");     // "—"

For calculations where tiny rounding differences matter (for example, financial back-end logic), consider doing the math in integers (cents) and apply toFixed only at the edge when presenting to users:

// Work in cents internally
const subtotalCents = 1995;    // €19.95
const taxCents = Math.round(subtotalCents * 0.25); // 25% VAT
const total = (subtotalCents + taxCents) / 100;
total.toFixed(2);  // presentation string like "24.94"

toFixed vs. toPrecision, toExponential, and toString

toFixed(d) locks in exactly d digits after the decimal. toPrecision(p) targets a total number of significant digits, automatically choosing fixed or exponential form based on the value and requested precision. toExponential always uses exponential notation, while toString picks the shortest accurate representation. The spec precisely defines the allowed ranges and the rounding behavior for all of these methods.

const x = 1234.567;

x.toFixed(2);       // "1234.57"  // fixed digits after decimal
x.toPrecision(3);   // "1.23e+3"  // 3 significant digits -> exponential
x.toExponential(2); // "1.23e+3"  // explicitly exponential
x.toString();       // "1234.567" // shortest accurate form

Debugging tips and patterns

When a toFixed result looks “off”, it’s almost always floating-point representation shining through. Verify the raw binary approximation or inspect more digits to see which way it should round; the MDN examples illustrate exactly this behavior with 2.55. If toFixed throws, double-check that your digits is an integer in the [0, 100] range. And if -0.00 is not acceptable in your UI, post-process the string as shown above.

Reference examples to keep handy

// Basic formatting
(0.004).toFixed(2);           // "0.00"
(123.456).toFixed(2);         // "123.46"
(123).toFixed(2);             // "123.00"

// Boundary values
(0.005).toFixed(2);           // "0.01"
(9999999999999999).toFixed(0) // "10000000000000000"

// Signed zeros
(-0.0004).toFixed(3);         // "-0.000"
(-0.0004).toFixed(3).replace(/^-0\./, "0."); // "0.000"

// Locale vs fixed
(12345.6).toFixed(2);         // "12345.60"
(12345.6).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
// "12,345.60"

// Guarding digits
function safeFixed(x, digits = 2) {
  const d = Math.trunc(digits);
  if (d < 0 || d > 100) throw new RangeError("digits out of range");
  return Number(x).toFixed(d);
}

References

SourceWhat it covers
MDN: Number.prototype.toFixed()Behavior overview, examples, limits, large-number notes, negative-number precedence, precision caveats.
ECMAScript® 2026 Language Specification — Number.prototype.toFixedAuthoritative algorithm: argument conversion, [0,100] range, rounding rule (tie picks larger n), sign handling, exponential fallback threshold.
MDN: RangeError — precision is out of rangeThe RangeError thrown by toFixed and related methods when precision arguments are out of bounds.
MDN: Number.prototype.toString()Relation between toFixed and toString for very large magnitudes and string forms.
MDN: Math.sign()Evidence of IEEE-754 signed zero in JavaScript, relevant to "-0.00" outcomes.