# How to expand \fpeval{} results numerically?

I' m trying to use xfp to create some automated homework solutions. For short documents it works like a charm, but for longer one the compiling time grows exponentially. For example:

\def\GDUbeta{30} % upstream beta
\def\GDUMa{3.0} % upstream Mach number
\def\GDUP{101} % upstream pressure
\def\GDUT{295} % upstream temp
%------ Modify initial values above this line ------
\def\GDUtheta{\fpeval{atand(2*cotd(\GDUbeta)*(\GDUMa^2*sind(\GDUbeta)^2-1)/( (1.4+cosd(2*\GDUbeta))*\GDUMa^2+2 ))}} % upstream theta
\def\GDUMan{\fpeval{\GDUMa*sind(\GDUbeta)}} % upstream Ma normal
\def\GDUPratio{\fpeval{1+2*1.4/(1.4+1)*(\GDUMan^2-1)}} % upstream pressure ratio
\def\GDUTratio{\fpeval{(1+2*1.4/(1.4+1)*(\GDUMan^2-1))*((2+(1.4-1)*\GDUMan^2)/((1.4+1)*\GDUMan^2))}} % upstream pressure ratio
\def\GDDMan{\fpeval{sqrt((2+(1.4-1)*\GDUMan^2)/(2*1.4*\GDUMan^2-(1.4-1)))}}
% downstream Ma normal
\def\GDDMa{\fpeval{\GDDMan/sind(\GDUbeta-\GDUtheta)}} % downstream Ma
% Closed form solution of beta --->
\def\GDtanmu{\fpeval{tan(asin(1/\GDDMa))}} % tan Mach angle
\def\GDClosec{\fpeval{\GDtanmu^2}}
\def\GDClosea{\fpeval{((1.4-1)/2+(1.4+1)/2*\GDClosec)*tand(\GDUtheta)}}
\def\GDCloseb{\fpeval{((1.4+1)/2+(1.4+3)/2*\GDClosec)*tand(\GDUtheta)}}
\def\GDClosed{\fpeval{sqrt(4*(1-3*\GDClosea*\GDCloseb)^3/((27*\GDClosea^2*\GDClosec+9*\GDClosea*\GDCloseb-2)^2)-1)}}
\def\GDClosee{\fpeval{(\GDCloseb+9*\GDClosea*\GDClosec)/(2*(1-3*\GDClosea*\GDCloseb))}}
\def\GDClosef{\fpeval{\GDClosed*(27*\GDClosea^2*\GDClosec+9*\GDClosea*\GDCloseb-2)/(6*\GDClosea*(1-3*\GDClosea*\GDCloseb))}}
\def\GDCloseg{\fpeval{tan(1/3*atan(1/\GDClosed))}}
\def\GDDbeta{\fpeval{atand(\GDClosee-\GDClosef*\GDCloseg)}}
% <--- Closed form solution of beta
\def\GDphi{\fpeval{\GDDbeta-\GDUtheta}} % angle of reflected shock
\def\GDRMa{\fpeval{\GDDMa*sind(\GDDbeta)}} % reflect shock Mach number
\def\GDRPratio{\fpeval{1+2*1.4/(1.4+1)*(\GDRMa^2-1)}} % reflect pressure ratio
\def\GDRTratio{\fpeval{\GDRPratio*((2+(1.4-1)*\GDRMa^2)/((1.4+1)*\GDRMa^2))}} % reflect temp ratio
\def\GDRMan{\fpeval{sqrt((2+(1.4-1)*\GDRMa^2)/(2*1.4*\GDRMa^2-(1.4-1)))}} % reflect pressure ratio
\def\GDRAMa{\fpeval{\GDRMan/sind(\GDphi)}} % reflected actual Mach number
\def\GDRP{\fpeval{\GDRPratio*\GDUPratio*\GDUP}} % reflected pressure
\def\GDRT{\fpeval{\GDRTratio*\GDUTratio*\GDUT}} % reflected temp
\def\GDSPratio{\fpeval{(1+(1.4-1)/2*\GDRAMa^2)^(1.4/(1.4-1))}} % stagnation pressure ratio
\def\GDSP{\fpeval{\GDSPratio*\GDRP/1000}} % stagnation pressure
\def\GDSTratio{\fpeval{1+(1.4-1)/2*\GDUMa^2}} % stagnation temp ratio
\def\GDST{\fpeval{\GDSTratio*\GDUT}} % stagnation temp
%------ DO NOT modify calculations above this line ------


The above calculation is 100% working, the problem is every time I insert these intermediate values in the homework solutions, it's calling all macros above it recursively and making it very very slow to compile.

I know this is kind of ugly coding in LaTeX, but is there any way to expand and cache fpeval to floating point numbers, just to avoid calling these macros for exponentially growing number of times?

• \expandafter\NextStep\expanded{{\fpeval{1+1}}} will result in \NextStep{2}. Commented Feb 9, 2023 at 6:40
• @Skillmon why not edef? Commented Feb 9, 2023 at 6:41
• If you're willing to move to the expl3 layer you can initialise and use fp-variables, so \ExplSyntaxOn\fp_new:N \l__guanyang_gdu_beta_fp \fp_set:Nn \l__guanyang_gdu_beta_fp {3.0}\ExplSyntaxOff Commented Feb 9, 2023 at 6:43
• @Rmano depends on what you need. If the first four macros never change then yes, \edef is the way to go. Commented Feb 9, 2023 at 6:45
• \edef is the solution for my case Commented Feb 11, 2023 at 19:55

The problem is that when you use, say \GDUtheta in \GDDMa, you're not applying the value, but the operations needed to compute it.

If you have carefully laid out the variables so each one only depends on the previous ones, you can do the evaluation.

I suggest to use the built-in methods.

\documentclass{article}

\ExplSyntaxOn

\NewDocumentCommand{\fpset}{mm}
{
\fp_zero_new:N #1
\fp_set:Nn #1 { #2 }
}
\NewExpandableDocumentCommand{\fpuse}{m}{\fp_use:N #1}

\ExplSyntaxOff

\fpset\GDUbeta{30} % upstream beta
\fpset\GDUMa{3.0} % upstream Mach number
\fpset\GDUP{101} % upstream pressure
\fpset\GDUT{295} % upstream temp
%------ Modify initial values above this line ------
\fpset\GDUtheta{atand(2*cotd(\GDUbeta)*(\GDUMa^2*sind(\GDUbeta)^2-1)/( (1.4+cosd(2*\GDUbeta))*\GDUMa^2+2 ))} % upstream theta
\fpset\GDUMan{\GDUMa*sind(\GDUbeta)} % upstream Ma normal
\fpset\GDUPratio{1+2*1.4/(1.4+1)*(\GDUMan^2-1)} % upstream pressure ratio
\fpset\GDUTratio{(1+2*1.4/(1.4+1)*(\GDUMan^2-1))*((2+(1.4-1)*\GDUMan^2)/((1.4+1)*\GDUMan^2))} % upstream pressure ratio
\fpset\GDDMan{sqrt((2+(1.4-1)*\GDUMan^2)/(2*1.4*\GDUMan^2-(1.4-1)))}
% downstream Ma normal
\fpset\GDDMa{\GDDMan/sind(\GDUbeta-\GDUtheta)} % downstream Ma
% Closed form solution of beta --->
\fpset\GDtanmu{tan(asin(1/\GDDMa))} % tan Mach angle
\fpset\GDClosec{\GDtanmu^2}
\fpset\GDClosea{((1.4-1)/2+(1.4+1)/2*\GDClosec)*tand(\GDUtheta)}
\fpset\GDCloseb{((1.4+1)/2+(1.4+3)/2*\GDClosec)*tand(\GDUtheta)}
\fpset\GDClosed{sqrt(4*(1-3*\GDClosea*\GDCloseb)^3/((27*\GDClosea^2*\GDClosec+9*\GDClosea*\GDCloseb-2)^2)-1)}
\fpset\GDClosee{(\GDCloseb+9*\GDClosea*\GDClosec)/(2*(1-3*\GDClosea*\GDCloseb))}
\fpset\GDClosef{\GDClosed*(27*\GDClosea^2*\GDClosec+9*\GDClosea*\GDCloseb-2)/(6*\GDClosea*(1-3*\GDClosea*\GDCloseb))}
\fpset\GDCloseg{tan(1/3*atan(1/\GDClosed))}
\fpset\GDDbeta{atand(\GDClosee-\GDClosef*\GDCloseg)}
% <--- Closed form solution of beta
\fpset\GDphi{\GDDbeta-\GDUtheta} % angle of reflected shock
\fpset\GDRMa{\GDDMa*sind(\GDDbeta)} % reflect shock Mach number
\fpset\GDRPratio{1+2*1.4/(1.4+1)*(\GDRMa^2-1)} % reflect pressure ratio
\fpset\GDRTratio{\GDRPratio*((2+(1.4-1)*\GDRMa^2)/((1.4+1)*\GDRMa^2))} % reflect temp ratio
\fpset\GDRMan{sqrt((2+(1.4-1)*\GDRMa^2)/(2*1.4*\GDRMa^2-(1.4-1)))} % reflect pressure ratio
\fpset\GDRAMa{\GDRMan/sind(\GDphi)} % reflected actual Mach number
\fpset\GDRP{\GDRPratio*\GDUPratio*\GDUP} % reflected pressure
\fpset\GDRT{\GDRTratio*\GDUTratio*\GDUT} % reflected temp
\fpset\GDSPratio{(1+(1.4-1)/2*\GDRAMa^2)^(1.4/(1.4-1))} % stagnation pressure ratio
\fpset\GDSP{\GDSPratio*\GDRP/1000} % stagnation pressure
\fpset\GDSTratio{1+(1.4-1)/2*\GDUMa^2} % stagnation temp ratio
\fpset\GDST{\GDSTratio*\GDUT} % stagnation temp
%------ DO NOT modify calculations above this line ------

\begin{document}

GDRPratio = \fpuse{\GDRPratio}

GDST = \fpuse{\GDST}

GDUT = \fpuse{\GDUT}

\end{document}


• Yes. This is a step-by-step homework solution. All variables only depend on previous variables. Commented Feb 11, 2023 at 20:01

If the calculations are static, that is, when you change a value you recompile and every value depends strictly only on previous calculated ones, you can try changing \def with \edef.

If not, you can try using pgfmath declare function instead, but the accuracy will suffer (sometimes a lot).

• You can use pgfmath-xfp to have the full precision of l3fp at least inside the function (though the input and result's precision is limited by pgfmath). Whether that really is an improvement is up to the user. Commented Feb 9, 2023 at 17:17