Blog ·

Between R$ 5,000 and R$ 7,350, half of every raise disappears from the 2026 payslip

Brazil's 2026 income tax exemption was written as a reducer on top of the old table. Inside one salary band the marginal rate reaches 51%. Here is the math, the code, and a one-cent rounding bug.

Between R$ 5,000 and R$ 7,350, half of every raise disappears from the 2026 payslip

Someone earning R$ 5,000 gross per month in Brazil pays no income tax in 2026. Someone earning R$ 6,000 pays R$ 385.10 a month. A thousand reais more in salary becomes R$ 385 more in income tax and R$ 140 more in social security. Of a R$ 1,000 raise, R$ 474.90 reaches the bank account.

I found this while building the 2026 net salary calculator and was sure it was my bug. It was not. It is how Law 15,270/2025 is written.

What actually changed

The monthly progressive table is the same one from Law 15,191/2025: exempt up to R$ 2,428.80, then 7.5%, 15%, 22.5% and 27.5%, each bracket with its own deductible amount. The new exemption "up to R$ 5,000" did not touch that table. It came in as a reducer applied on top of the tax already calculated:

  • gross salary up to R$ 5,000.00: a reducer of R$ 312.89, which is exactly the tax owed at R$ 5,000 with the simplified deduction, so the tax becomes zero;
  • between R$ 5,000.01 and R$ 7,350.00: reducer = 978.62 − 0.133145 × gross salary, never more than the tax itself;
  • above R$ 7,350.00: nothing.

Monthly income tax in 2026 by gross salary. The dashed line is the table, the solid line is what leaves the payslip. Between R$ 5,000 and R$ 7,350 the two lines separate and meet again.

Inside the band, the reducer shrinks by about R$ 133 for every R$ 1,000 of extra salary. That stacks on the 27.5% of the top table bracket and the 14% of the top social security (INSS) bracket. In practice, someone between R$ 5,000 and R$ 7,350 faces a marginal rate close to 50%, higher than someone earning R$ 10,000, because above R$ 8,475.55 the INSS contribution is capped at R$ 988.09.

Some points on the curve, no dependents, no alimony:

gross INSS tax by the table reducer tax paid net
3,000.00 248.60 0.00 0.00 0.00 2,751.40
5,000.00 501.51 312.89 312.89 0.00 4,498.49
6,000.00 641.51 564.85 179.75 385.10 4,973.39
7,000.00 781.51 801.35 46.61 754.74 5,463.75
7,350.00 830.51 884.13 0.00 884.13 5,635.36
10,000.00 988.09 1,569.55 0.00 1,569.55 7,442.36

Take the R$ 6,000 row and the R$ 7,000 row. Gross goes up R$ 1,000, net goes up R$ 490.36. The exact share kept by the government inside that band is 50.96%: 14% INSS, plus 27.5% income tax on the 86% that remains after INSS, plus the 13.31% of reducer that fades away.

My opinion: this is a design flaw, not a policy choice. A reducer that fades linearly creates a band where each extra real is worth less than it is for someone who already earns twice as much. One can argue it is temporary and the band is narrow. It is still the band where a large share of the country's junior and mid-level developers sit.

The code

The whole rule fits in one function. This is TypeScript, from the calculator's solver:

export const REDUCER_FULL_AMOUNT_2026 = 312.89;
export const REDUCER_INTERCEPT_2026 = 978.62;
export const REDUCER_SLOPE_2026 = 0.133145;

export function calcRedutor2026(bruto: number): number {
  if (bruto <= 5000) return REDUCER_FULL_AMOUNT_2026;
  if (bruto <= 7350) {
    return roundCents(Math.max(0, REDUCER_INTERCEPT_2026 - REDUCER_SLOPE_2026 * bruto));
  }
  return 0;
}

// later, in the full calculation:
const redutor = roundCents(Math.min(irTabela, calcRedutor2026(bruto)));
const irrf = roundCents(irTabela - redutor);

The Math.min matters. At R$ 4,000 the table gives R$ 114.76 of tax and the reducer is "worth" R$ 312.89, but it can only zero the tax, not refund it.

The bug I actually found

It was not in the law. It was in roundCents.

The first INSS bracket in 2026 goes up to R$ 1,621.00, the minimum wage, at 7.5%. In the calculator, 1,621 × 0.075 came out as R$ 121.57. The tax authority says R$ 121.58. In JavaScript, 1621 * 0.075 is 121.57499999999999, and Math.round(121.574999 * 100) / 100 rounds down. One cent, on the first row of the table, on the value most people look up.

The fix is ugly and works:

const roundCents = (v: number) => Math.round(v * 100 + 1e-6) / 100;

If you maintain any payroll calculation in JavaScript, look for this pattern. It shows up at every bracket boundary that ends in 5 in the third decimal place.

What the calculator does not cover

Only the monthly CLT salary: progressive INSS, income tax with the legal or the simplified deduction (whichever gives less tax), dependents, court-ordered alimony and other declared deductions. It does not calculate the 13th salary, vacation pay, overtime, profit sharing or owner's pay. For people with dependents, the legal deduction only starts to win from four dependents; before that the simplified R$ 607.20 deduction wins.

Sources: the Receita Federal page on 2026 taxation, which carries the table and the reducer formula, and Portaria MPS/MF 13/2026 for the INSS brackets.

The numbers and the chart come from calculy.net/finance/salario-liquido-2026. If your January payslip differs from what it shows, tell me the gross and the difference. The calculation is deterministic and I want to know where it is wrong.

Half of every raise disappears between R$ 5,000 and R$ 7,350 in 2026 — Calculy