E
E
Evgeny Ivanov2017-03-06 07:35:23
Delphi
Evgeny Ivanov, 2017-03-06 07:35:23

How to change the data format transmitted from Excel?

I am writing a program that works with an Excel file.
The task is to read its contents and write it to an array.
Everything works, but some of the data is distorted when writing to the array. When reading from a particular column.
For example - Excel in the file (in the column) says 6651440820000450, and the array receives the value 6.67716087900069E+15.
In Excel in a file, the cells in this column are of type numeric.
Moreover, other similar cells containing less long numbers are written to the array correctly, without distortion.
How to fix it?
Part of the code

var
Ap:Variant; //Переменная excel файла
TABLE_list_1 : array of array [1..50] of string; //Массив

begin
Ap:=CreateOleObject('Excel.Application'); //Создаем объект
Ap.Workbooks.Open(input_file_path,0,True); //Открываем файл для чтения
Ap.DisplayAlerts:=False; //Отключаем любые сообщения excel

SetLength(TABLE_list_1, 1000000); //Указываем размер массива 
//Он не динамический т.к. в Delphi 7 есть ошибка при больших динамических массивах

//Передаем содержимое ячейки Excel в элемент массива
for i:=1 to 50 do
begin
for j:=1 to 50 do
begin
TABLE_list_1[TABLE_list_1_records_count,j]:=Ap.Cells[i,TABLE_list_1_column_number[j]];
end;
end;
Ap.Application.Quit; //Закрываем excel файл

Answer the question

In order to leave comments, you need to log in

2 answer(s)
M
Mercury13, 2017-03-06
@Mercury13

Swimming, we know. The array receives just what is needed. The reason is in the number → string conversion algorithms, and you need to add this thing:
“If the number is of type Double, in fact an exact integer and is within certain limits, convert it to Int64, and then Int64 to a string.”
The very test to see if double is an exact integer is rather tricky. I have code for this in C++ (which in turn is a port of code from Java). Will you port to Delphi yourself?

enum {
    DOUBLE_MANTISSA_BITS = 52,
    DOUBLE_MIN_EXPONENT = -1022,
    DOUBLE_EXPONENT_BIAS = 1023,
};

#define UC8x(X,Y,Z,T) (static_cast<uint64_t>(0x##X##Y##Z##T##ULL))
#define DOUBLE_MANTISSA_MASK     UC8x(000f, ffff, ffff, ffff)
#define DOUBLE_EXPONENT_MASK     UC8x(7ff0, 0000, 0000, 0000)
#define DOUBLE_EXPONENT_MASK_HI  UC4x(7ff0, 0000)
#define DOUBLE_IMPLICIT_BIT (DOUBLE_MANTISSA_MASK + 1)

    union DoubleInt {
        double asDouble;
        uint64_t asInt;
        struct {
            uint32_t lo, hi;
        } asIntel;
    ....
   }

    int DoubleInt::exponent() const
    {
        return static_cast<int>((
                      static_cast<int64_t>(asInt & DOUBLE_EXPONENT_MASK) >>
                      (DOUBLE_MANTISSA_BITS)) - DOUBLE_EXPONENT_BIAS);
    }

    uint64_t DoubleInt::unsafeMantissa() const
    {
        int ex = exponent();
        uint64_t bits = asInt & DOUBLE_MANTISSA_MASK;
        return (ex == DOUBLE_MIN_EXPONENT - 1)
          ? bits << 1
          : bits | DOUBLE_IMPLICIT_BIT;
    }

    bool DoubleInt::isPreciseInteger() const
    {
        // функцию isFinite() намеренно упустил, ибо она есть «в коробке» Delphi.
        // А вот в Си++ есть не во всех реализациях…
        return isFinite()
            && (asDouble == 0.0 ||
            DOUBLE_MANTISSA_BITS - numberOfTrailingZeros(unsafeMantissa()) <= exponent());
    }

int math::numberOfTrailingZeros(uint64_t i)
{
    uint32_t x, y;
    if (i == 0u) return 64;
    int n = 63;
    y = static_cast<uint32_t>(i);
    if (y != 0) { n = n -32; x = y; } else x = (int)(i >> 32);
    y = x << 16; if (y != 0) { n = n -16; x = y; }
    y = x << 8;  if (y != 0) { n = n - 8; x = y; }
    y = x << 4;  if (y != 0) { n = n - 4; x = y; }
    y = x << 2;  if (y != 0) { n = n - 2; x = y; }
    return n - ((x << 1) >> 31);
}

bool math::isPreciseInteger(double x)
{
    DoubleInt di;
    di.asDouble = x;
    return di.isPreciseInteger();
}

Maybe simplified options like "column X - convert to Int64, then to integer" will work for you. This was lacking in the industrial program.

K
kalapanga, 2017-03-06
@kalapanga

var
  x: Int64;
...
  x := Ap.Cells[i,j];
  TABLE_list_1[i,j]:=IntToStr(x);
...

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question