Mathematica中的Put-Get循环总是确定性的



Mathematica中,与其他计算机数学系统一样,数字在内部以二进制形式存储。但是,当使用PutPutAppend等函数导出时,它们会转换为近似小数。当你用Get这样的函数将它们导入回来时,它们会从这种近似的十进制表示恢复为二进制形式

问题是,恢复的数字是否总是与原始二进制数字相同,如果不是总是,在哪些情况下不是,差异有多大?我对Put-Get循环(在同一计算机系统上)特别感兴趣。

以下两个简单的实验表明,即使对于任意精度的数字,Mathematica中的Put-Get循环也可能总是准确地恢复原始数字:

In[1]:= list=RandomReal[{-10^6,10^6},10000];
Put[list,"test.txt"];
list2=Get["test.txt"];
Order[list,list2]===0
Order[Total@Abs[list-list2],0.]===0
Out[4]= True
Out[5]= True

In[6]:= list=SetPrecision[RandomReal[{-10^6,10^6},10000],50];
Put[list,"test.txt"];
list2=Get["test.txt"];
Order[list,list2]===0
Total@Abs[list-list2]//InputForm
Out[9]= True
Out[10]//InputForm=
0``39.999515496936205

但也许我错过了什么?


更新

通过更正确的测试代码,我发现在现实中,这些测试只表明恢复的数字具有相同的二进制RealDigits,但它们的Precision甚至可能在Equal意义上不同。以下是更正确的测试:

test := (Put[list, "test.txt"];
  list2 = Get["test.txt"];
  {Order[list, list2] === 0,
   Order[Total@Abs[list - list2], 0.] === 0,
   Total[Order @@@ RealDigits[Transpose[{list, list2}], 2]],
   Total[Order @@@ Map[Precision, Transpose[{list, list2}], {-1}]],
   Total[1 - Boole[Equal @@@ Map[Precision, Transpose[{list, list2}], {-1}]]]})
In[8]:= list=RandomReal[NormalDistribution[],10000]^1001;
test
Out[9]= {False,True,0,1,3}
In[6]:= list=RandomReal[NormalDistribution[],10000,WorkingPrecision->50]^1001;
test
Out[7]= {False,False,0,-2174,1}

恐怕我不能给出一个明确的答案。如果您查看文本文件,您会发现它存储为值的InputForm,包括非机器精度数字的精度指示。

假设Get使用与ImportStringExportString相同的转换例程,您的测试可以稍微加快一点。

Monitor[
 Do[
  i = RandomReal[{$MinMachineNumber, 10 $MinMachineNumber}, 100000];
  If[i =!= 
    ToExpression[ImportString[ExportString[i, "Text"], "List"]], 
   Print[i]], {n, 100}
  ],
 n]

我已经对数亿个介于$MinMachineNumber和$MaxMachineNumber之间的数字进行了测试,我总是能得到原始数字。当然,这不是证据,但如果有,你似乎不太可能看到不真实的数字(在这种情况下,差异非常小,可以忽略不计)。

需要知道的一件重要的事情是Put[]/Get[]不会保持压缩数组的压缩。您应该查看DumpSave[]。它的速度要快得多,因为它是一种二进制格式,并保持数组的压缩。

最新更新