Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I am trying to copy some numerical values of parameters from a plain text file into Mathematica, then arranging them in a list and exporting them to another text file, with .ini extension.

My problem is that I want the exact same precision for the output file, as the one I have in the input, but Mathematica is truncating the output, no matter what I do. I am aware of this question Annoying display truncation of numerical results, but the methods there are not working for me.

This is what I am trying:

a = 70.3029753444001
70.303
bparam = 0.225980870923660
0.225981
paramlist = {aparam, bparam}

This is my exporting function:

exportText[parlist_, 
name_] := (outtext = {"a = " <> ToString[parlist[[1]]],
"b = " <> ToString[parlist[[2]]]};
Export["./" <> ToString[name] <> ".ini", outtext, "Text"])

And now I export:

exportText[paramlist, "param1"]

The output in my param1.ini file is of course:

a = 70.303
b = 0.225981

I cannot use:

SetOptions[InputNotebook[], PrintPrecision-> 10]

Because I want for each number I copy into Mathematica, the same precision in the output, and all parameters have different precisions.

Also using NumberForm as was suggested to me in the other post, ruins my output:

exportText[NumberForm[#, {10, 7}] &@paramlist, "param1"]

The file looks like:

a = {70.303, 0.225981}
b = {10, 7}

And anyway this is not dynamical, for any number I copy into Mathematica.

I tried to create a dynamical function:

setPrecisionOfNumber = 
SetPrecision[#, Length[RealDigits[#][[1]]] - RealDigits[#][[2]]] &;

But then this adds extra zeros at the end, for example:

setPrecisionOfNumber@bparam
0.2259808709236600

setPrecisionOfNumber@0.2541
0.2541000000000000

So what is the solution you propose? I have of course tried things like N[aparam,10] but it doesn't work.

And anyway I need it to be dynamical.

I would be grateful for ideas on how to solve this.

EDIT Using @billisphere solution, I changed my exportText function definition to:

exportText[parlist_, 
name_] := (outtext = {"a = " <> 
ToString[parlist[[1]], InputForm, NumberMarks -> False],
"b = " <> ToString[parlist[[2]], InputForm]};
stringfile = ExportString[outtext, "Text"];
Export["./" <> ToString[name] <> ".ini", outtext, "Text"])

I still want to understand certain things of number representation. As you can see, if I don't use NumberMakrs->False, I get for a number like:

bparam = 0.225980870923660011

The following prints in my file:

FilePrint["./param1.ini"]  

a = 70.3029753444001
b = 0.225980870923660011`17.35407167806817

Using NumberMakrs->False also for the bparam, I get:

FilePrint["./param1.ini"]
a = 70.3029753444001
b = 0.225980870923660011

I still want to understand why there are still truncations, if I have a number like:

bparam = 0.22598087092366001

The printed file is then:

FilePrint["./param1.ini"]  

a = 70.3029753444001
b = 0.22598087092366

So, the 001 at the end of bparam was truncated. If I add another 1 at the end of bparam, I don't get any truncation.

I know these numbers are quite large with many decimal digits, and in the praxis I don't need this. But I want to be sure that my output is the same as my input in all cases.

share|improve this question
2  
if you want the output precision precisely the same as input one approach is to simply read in as strings, do your manipulation and write the strings back out. Show an example of the input if you want help with that. –  george2079 Apr 29 at 17:08

2 Answers 2

up vote 2 down vote accepted

You could use ToString[_, InputForm] when constructing the output, like this (slightly modified from your original for demo purposes):

In[23]:= a = 70.3029753444001
b = 0.225980870923660
paramlist = {a, b}

Out[23]= 70.303

Out[24]= 0.225981

Out[25]= {70.303, 0.225981}

In[26]:= exportText[parlist_] :=
 Module[
  {outtext =
    {"a = " <> ToString[parlist[[1]], InputForm],
     "b = " <> ToString[parlist[[2]], InputForm]}},
  ExportString[outtext, "Text"]
  ]

In[27]:= exportText[paramlist]

Out[27]= "a = 70.3029753444001
b = 0.22598087092366"

For your updated question, about how some digits are truncated despite stringifying with InputForm, there's the following explanation at ref/$MachinePrecision (slightly reformatted):

Precision is based on the number of digits when more than Ceiling[$MachinePrecision] + 1 are entered.

In[53]:= b = 0.22598087092366001
ToString[b, InputForm]

Out[53]= 0.225981

Out[54]= "0.22598087092366"

In[55]:= b = 0.225980870923660010
ToString[b, InputForm]

Out[55]= 0.225980870923660010

Out[56]= "0.22598087092366001`17.35407167806817"

In[57]:= Ceiling[$MachinePrecision] + 1

Out[57]= 17

The first number input has 17 digits and the second has 18 digits, thus the difference in representation. (More info about how Mathematica decides this for numeric input can be found here.)

As for how to work around this when you really need to avoid any truncation at all, you could Import your input as strings and use Characters/PadRight/StringJoin to adjust each number string with enough trailing zeros before letting Mathematica's evaluator interpret the strings as numbers.

For example, you could do something like what I've done below with the function pad. (I've included some testing code as well.)

In[1]:= ss =
  {"0.000012341234123412341",
   "0.0000123412341234123412",
   "0.0000123412341234",
   "00001234",
   "0000.000012341234",
   "0.000012340000",
   "000012340000"};

In[2]:= nonzero = CharacterRange["1", "9"];

In[3]:= regularize[numberString_] :=
 StringReplace[numberString,
  {StartOfString ~~ "0" ... ~~ "." :> "0.",
   StartOfString ~~ "0" .. ~~ d : nonzero .. :> d,
   "0" .. ~~ EndOfString /; ! StringFreeQ[numberString, "."] :> ""}
  ]

In[4]:= checkRoundtrip[numberString_, preprocess_: Identity] :=
 ToString[
   ToExpression@preprocess@numberString,
   InputForm,
   NumberMarks -> False
   ] === regularize@numberString

In[5]:= checkRoundtrip /@ ss

Out[5]= {False, True, True, True, True, True, True}

In[6]:= limit = Ceiling@$MachinePrecision + 2;
padAboveLimit[digits_] :=
 StringJoin@PadRight[Characters@digits, limit, "0"]

In[8]:= leadingZeros = "0" ... ~~ Repeated[".", 1] ~~ "0" ...;
significantDigits = nonzero ~~ DigitCharacter ...;

In[10]:= pad[s_] :=
 StringReplace[s,
  zs : leadingZeros ~~ ds : significantDigits :>
   zs <> padAboveLimit@ds
  ]

In[11]:= checkRoundtrip[#, pad] & /@ ss

Out[11]= {True, True, True, True, True, True, True}
share|improve this answer
    
Thanks for your answer, I hadn't noticed the usefulness of the option InputForm. I think this solution is enough for my practical needs. Nevertheless, there are still some strange truncations or printing of NumberMarks, which I would like to understand better. I will edit my question to add this too. –  Santi Apr 30 at 10:59
    
@Santi, you're right that there's more to this. I investigated a little bit and found some info. See my updated answer. –  billisphere Apr 30 at 16:08

Here is a very simple example sorting a file based on the numeric value of the first column, but retaining the exact string representation.

 FilePrint["data.txt"]

213.2 324534.

3.000000000 454

42 1E50 #comment

 data = StringSplit[Import["data.txt", "Text"], "\n"];
 Export["outfile" ,  SortBy[ data ,
     Read[StringToStream@First@StringSplit@#, Number] &] , "Text"];
 FilePrint["outfile"]

3.000000000 454

42 1E50 #comment

213.2 324534.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.