Here's a little Matlab code to help you compute the after-tax return on an investment in different types of accounts. The original purpose of this matlab code is explained in this comment thread in r/PF, with an improvement based on this comment thread.

The formulas I used are in the code below, but in general it's ((1+M)*(1-CMIT)*((R-ER).^Y)*(1-RMIT)-1)*(1-RCGT) where M is the match (1), CMIT is the current income tax rate (0.25), RMIT is the retirement income tax rate, RCGT is the retirement capital gains tax rate, R is the market return (1.07), and E is the expense ratio (0.02). If you think that's wrong or I'm leaving something out, post to r/PF or PM plexluthor there.

It makes images like this: Plot

Matlab code I used (which should also work in Octave and FreeMat):

function returnPlotter()

    Y = 1:3:55; % years invested; longer favors IRA
    R = 1.01; % annual return; doesn't appear to matter, actually.
    CMIT = .15; % current marginal tax rate; higher favors 401k
    TradCMIT = 0;
    RothCMIT = CMIT;
    RMIT = .15; % retirement marginal income tax rate
    TradRMIT = RMIT;
    RothRMIT = 0;
    RCGT = .15; % retirement capital gains tax rate
    TradRCGT = 0;
    RothRCGT = 0;
    ER = .02; % additional expense ratio (e.g., in 401k); lower favors 401k
    Non401kER = 0;
    M = 1; % contribution match
    NonM = 0;

    figure
    legendStr = {};

    plot(Y,getReturns(Y, R, TradCMIT, TradRMIT, TradRCGT, ER, M),'o-','LineWidth',2);
    legendStr{end+1} = sprintf('Traditional 401k with %.0f%% match',M*100);
    hold all

    plot(Y,getReturns(Y, R, RothCMIT, RothRMIT, RothRCGT, ER, M),'+-.','LineWidth',2);
    legendStr{end+1} = sprintf('Roth 401k with %.0f%% match',M*100);

    plot(Y,getReturns(Y, R, TradCMIT, TradRMIT, TradRCGT, ER, NonM),'o-','LineWidth',2);
    legendStr{end+1} = 'Traditional 401k without match';

    plot(Y,getReturns(Y, R, RothCMIT, RothRMIT, RothRCGT, ER, NonM),'+-.','LineWidth',2);
    legendStr{end+1} = 'Roth 401k without match';

    plot(Y,getReturns(Y, R, TradCMIT, TradRMIT, TradRCGT, Non401kER, NonM),'o-','LineWidth',2);
    legendStr{end+1} = 'Traditional IRA';

    plot(Y,getReturns(Y, R, RothCMIT, RothRMIT, RothRCGT, Non401kER, NonM),'+-.','LineWidth',2);
    legendStr{end+1} = 'Roth IRA';

    plot(Y,getReturns(Y, R, CMIT, RMIT, RCGT, Non401kER, NonM),'LineWidth',2);
    legendStr{end+1} = 'Taxable Account';

    xlabel('Years Until Distribution')
    ylabel('Total Return')
    title(sprintf(['Market Return/Year: %.2f, Expense Ratio in 401k: %.2f, Marginal Tax Rate: %.2f\n'...
        'Retirement Marginal Tax Rate: %.2f, Retirement Capital Gains Rate: %.2f'],...
        R,ER,CMIT,RMIT, RCGT));
    legend(legendStr,'Location','Best')
    print(gcf,'-dpng','-r150',sprintf('R%.2fE%.2fT%.2f.png',R,ER,CMIT))

end

function returns = getReturns(Y, R, CMIT, RMIT, RCGT, ER, M)
    returns = ((1+M)*(1-CMIT)*((R-ER).^Y)*(1-RMIT)-1)*(1-RCGT);
end
changed March 12, 2013 delete history edit