# Unexpected behavior: CForm converts rationals to floating numbers

One line question: How to make CForm[1/2] generate 1/2 instead of 0.5? (Thanks Nasser's comment!)

I've been using CForm to convert Mathematica expressions to C codes for a long time. Although the built-in C strings are a little bit annoying (e.g., Sqrt[2] is translated to "Sqrt(2)" instead of "sqrt(2)"), it works.

However, recently I found an even more annoying behavior which I cannot find a workaround so far. Consider converting the following simple piece extracted from a rather complicated expression

3/40007 + (2 test )/40007//CForm


0.00007498687729647311 + (2*test)/40007.

which is totally bizarre! What I expect is simply

3/40007. + (2*test)/40007.

which is trivial yet crucial to my following work. (To be clear, I don't mean the presence of the decimal point is crucial! Rather, I mean the form of numbers should no be changed.) Can anyone come up with a solution, at least to tell CForm stop doing such a floating number conversion? Certainly I could manually rewrite this piece (or use Together) like this

(3 + 2 test)/40007 // CForm


(3 + 2*test)/40007.

But given that I have tons of complicated expressions to be converted, it is super inefficient for sure. Besides there's a possibility that test = I, and 3/40007 + (2 I)/40007 // Together returns itself.

PS. It is surprising that the discussions related to CForm and other C utilities are so rare on this site! Am I missing something?

-
I think you are asking Mathematica to read your mind. We don't have the input by direct thought user interface yet, so you will have to write things like 3. and 40007. when they are critical to the interpretation you need. –  m_goldberg Jul 31 '13 at 1:55
@m_goldberg, I'm sorry but I have no idea what you're talking about...maybe you misunderstand my question? –  Leo Fang Jul 31 '13 at 1:59
Maybe I did, but I find it bizarre that you reject 0.00007498687729647311 + (2*test)/40007., which is what C would make of 3/40007. + (2 test )/40007. –  m_goldberg Jul 31 '13 at 2:03
In short, I expect CForm to convert an Mathematica expression to a piece of C code (in the form of strings) without any pre-processing of the form of numbers, and this is how I used it in the past. –  Leo Fang Jul 31 '13 at 2:03
@m_goldberg, oh yes, I reject it for some accuracy and efficiency concerns...I may be wrong about it, but I think it would be better to keep 3/40007. instead of 0.00007498687729647311, at least that's what I expect CForm to do. –  Leo Fang Jul 31 '13 at 2:07

Warning: The conversion below can lead to wrong results in the C-code. Be aware of that.

Since you complain anyway about "Sqrt" and "Pow" have you considered writing your own cform? The real CForm seems a bit stubborn about the numerical evaluation of rational expressions and with your own version you can cure this too. A very basic hack could look like

cform[Rational[a_, b_]] := "(" <> cform[a] <> "/" <> cform[b] <> ")";
cform[Power[a_, b_]] := "pow(" <> cform[a] <> "," <> cform[b] <> ")";
cform[Sqrt[a_]] := "sqrt(" <> cform[a] <> ")";
cform[Plus[a_, b_]] := "(" <> cform[a] <> " + " <> cform[b] <> ")";
cform[Times[a_, b_]] := "(" <> cform[a] <> " * " <> cform[b] <> ")";
cform[a_?AtomQ] := ToString[a];


and then you go

3/40007 + (2 test)/40007 // cform
(* "((3/40007) + ((2/40007) * test))" *)


or even

Series[Sin[x + y], {x, 0, 3}, {y, 0, 3}] // Normal // cform
(*
(x + (((-1/6) * pow(x,3)) + (((1 + ((-1/2) * pow(x,2))) * y) +
(((((-1/2) * x) + ((1/12) * pow(x,3))) * pow(y,2)) + (((-1/6) +
((1/12) * pow(x,2))) * pow(y,3))))))
*)


Maybe you can upload a larger expression to pastebin.com so that we have a better feeling, what kinds of expressions you try to convert.

There are at least two thing you could make easier for yourself. First is, that all functions which are real function calls in C too, like cexp, sqrt, abs, ... probably don't need all their own rule, because the only important thing is that the function names are converted. Then the arguments are just supplied separated by a comma in parenthesis. Therefore, you could create a general rule and then you have to provide the conversion of the function name only.

Additionally, for everything unmatched, you probably want to call ToString[...,CForm] to make it convert at least to something. Here is your version striped of the pow and exp rules and I added 4 rules at the end:

cform[Rational[a_, b_]] := cform[a] <> "/" <> cform[b] <> ".";
cform[Complex[a_, b_]] :=
"(" <> If[cform[a] == "0", "", cform[a]] <> "+" <> cform[b] <> "*I" <>
")"; cform[Times[a_, b_]] :=
"(" <> cform[a] <> ")" <> " * " <> "(" <> cform[b] <> ")";
cform[Plus[a_, b_]] := cform[a] <> " + " <> cform[b];
cform[a_?AtomQ] := ToString[a];
cform[h_[args__]] :=
cform[h] <> "(" <> StringJoin[Riffle[cform /@ {args}, ","]] <> ")";
cform[Power] = "pow";
cform[Exp] = "cexp";
cform[Sqrt] = "sqrt";
cform[allOther_] := ToString[allOther, CForm];

-
@LeoFang No, you didn't. I did. Actually, the order of the rules is important since Rational is an atom too. Try again. –  halirutan Jul 31 '13 at 4:39
@OleksandrR. I never used CForm too and this is really just a hack to get the desired output without thinking about the consequences. Something like 1/2 is quite dangerous because you divide two int. I'll have a look at this post tomorrow. Pretty early here and I haven't slept. –  halirutan Jul 31 '13 at 4:43
@OleksandrR. I've rarely used CForm directly, but I have used Splice which essentially uses CForm. I didn't notice the behavior with Rational before, likely because I was using floats. (Testing a c++ version of WignerJ.) –  rcollyer Jul 31 '13 at 19:11
@LeoFang the docs for Splice are pretty good, there are a couple of subtleties, though. So, I'd recommend trying to something modest, and seeing how it goes. Then ask any questions that arise. Now integrating into it the overall workflow is a bit more interesting, but make rules should be straightforward to generate. I have running around here code for integrating mprep into boost build, but it has been a while since I touched it. –  rcollyer Jul 31 '13 at 21:03
@LeoFang oh, and as to not realizing that Splice exists, I saw an answer the other day with a function that has been in there since v1, and in my nearly 20 years of use of Mathematica I've never run across it. And, I used to dig through the docs to find interesting things. –  rcollyer Jul 31 '13 at 21:05