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

MATLAB conversion: CELL to DOUBLE; But numbers change

我正在读取 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

我收到了这个错误:

Warning: Support of strings that are not valid variable names or define a number will be removed in a future release. To create symbolic expressions, first create symbolic variables and then use operations on them.
In sym>convertExpression (line 1536)
In sym>convertChar (line 1441)
In sym>tomupad (line 1198)
In sym (line 177)
In cell2sym (line 28)
In sym>tomupad (line 1208)
In sym (line 177)
In vpa (line 44)
Error using symengine
New array must have the same number of elements as the original array.

Error in sym/reshape (line 50)
ySym = mupadmex('symobj::reshape',x.s,args{:});

Error in cell2sym (line 34)
S = reshape(sym([Csym{:}]), size(C));

Error in sym>tomupad (line 1208)
xsym = cell2sym(x);

Error in sym (line 177)
S.s = tomupad(x);

Error in vpa (line 44)
ss = sym(s);

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

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

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

这很可能会降低性能,但这是精确表示的回报(直到 MATLAB 本身支持 128 位浮点,这可能在时间上很遥远,而且性能更密集)。

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

更好的答案

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

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

Textscan 已经检查了指数和小数,直接进入整数 class 而不通过 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 中的位置转换数字 table)和 str2uint64('1000000000000000000') = 18446744073709551615(溢出)。使用风险自负。