MATLAB转换:CELL到DOUBLE;但数字会改变

时间:2016-12-06 11:50:55

标签: excel matlab char double cell

我正在阅读excel文件。在其中一列(仅包含很长的数字)中,MATLAB将其作为CELL导入(因为它有一个标题)。

这是导入的样本:
' 980000684210053338'

这些是我的尝试:

转换为DOUBLE但数字更改
tableM.v1 = cellfun(@str2num,tableM.v1);

转换为DOUBLE但数字更改
tableM.v1 = cellfun(@str2double,tableM.v1);

转换为CHAR但数字正确
tableM.v1 = cell2mat(tableM.v1);

如何在保持正确值的同时将此CELL转换为DOUBLE?

由于

P.S。我正在使用MATLAB R2016a。

更新
我从答案中运行了这段代码:
tableM.v1 = vpa(tableM.v1); % assuming tableM.v1 is a cellstr

我收到了这个错误:

  

警告:支持无效变量名称的字符串或   定义一个号码将在以后的版本中删除。创造   符号表达式,首先创建符号变量然后   对它们使用操作。
  在sym> convertExpression(第1536行)中     在sym> convertChar(第1441行)中     在sym> tomupad(第1198行)中     在sym(第177行)
    在cell2sym(第28行)中     在sym> tomupad(第1208行)中     在sym(第177行)
    在vpa(第44行)
  使用symengine时出错   新数组必须与原始数组具有相同数量的元素   阵列。

     

sym / reshape中的错误(第50行)
  ySym = mupadmex(' symobj :: reshape',x.s,args {:});

     

cell2sym中的错误(第34行)
      S =重塑(sym([Csym {:}]),大小(C));

     

sym> tomupad中的错误(第1208行)
      xsym = cell2sym(x);

     

sym中的错误(第177行)
                  S.s = tomupad(x);

     

vpa中的错误(第44行)
      ss = sym(s);

2 个答案:

答案 0 :(得分:1)

双精度数最多15 stable decimal placesMathWorks puts it,“double值不能正确表示大于2 53 的所有整数”。由于Excel数字长度为18位有效小数位,因此double转换的精度损失是不可避免的。

为避免精度损失,您可以将字符串转换为使用variable precision arithmetic的数字:

 tableM.v1 = vpa(tableM.v1); % assuming tableM.v1 is a cellstr

这很可能会扼杀性能,但这是精确表示的回报(直到MATLAB本身支持128位浮点,这可能是时间太长而且性能密集程度更高)。

理论上,uint64也可以准确地保存整数,但似乎没有一种干净的方法将字符串转换为我能找到的 n 位整数

答案 1 :(得分:1)

更好的答案

下面的回答是接受的答案,但在拖延某事时,我意识到它太聪明了。我认为您真正想要的是使用textscan

tableM.v1 = cellfun(@(x) textscan(x, '%u64'), tableM.v1);

Textscan已经检查了指数和小数,直接转到整数类而不通过double,并且正确溢出(下面的溢出示例不太正确,因为指数向量也溢出。最大uint64是实际上是9223372036854775807)。然而,不是一个漂亮的向量,你最终将得到一个数字的单元格数组,因为这是textscan吐出的内容。任何格式错误的数字都会导致空单元格,在转换为向量之前,您必须处理这些单元格。

>> in = {'cat', '1e10', '980000684210053338};
>> out = cellfun(@(x) textscan(x, '%u64'), in)
out =

  1×3 cell array

  [0×1 uint64]    [10000000000]    [980000684210053338]

修复后,您可以使用cell2mat转换为矢量。

原始答案

正如其他答案所指出的那样,由于精度损失,双打不能保持这些数字。您需要将它们转换为64位整数,而无需先通过double过滤器。试试这个三行函数:

function out = str2uint64(in)
    % Convert the digits into an array of numbers and cast to
    % uint64
    in = uint64(in - 48);

    % Create the order of magnitude for each digit and convert
    % that also to uint64
    exponents = uint64(logspace(numel(in)-1, 0, numel(in)));

    % Why would sum default to convert your numbers to doubles?!?
    % The 'native' tag is recent, I believe, but if you have it,
    % it will preserve the data type.
    out = sum(in .* exponents, 'native');
end

使用:

tableM.v1 = cellfun(@str2uint64,tableM.v1);

有一点需要注意的是,由于某些愚蠢的原因,当MATLAB对数字求和时,它会将它们转换为double。在当前版本的R2016b中,有一个标志告诉它在没有强制转换的情况下求和。我不知道那个国旗何时被释放,所以你的里程可能会有所不同。如果您没有该选项,则必须在循环中进行总和。

另一个警告:此函数没有输入或输出检查,因此str2uint64('cat') = 5658(我只是根据它们在ASCII表中的位置转换数字)和str2uint64('1000000000000000000') = 18446744073709551615(溢出)。使用风险自负。