Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2017.01.15;
Скачать: CL | DM;

Вниз

FloatToTextFmt возвращает -922337203685477.5808   Найти похожие ветки 

 
Skyle ©   (2012-07-26 08:28) [0]

Всем привет.

D2010, многопоточное приложение. По содержимому TClientDataSet генерируем SQL для вставки в БД. Код примерно такой


while not cds.eof do
begin
 S := "";
 for I := 0 to cds.Fields.Count - 1 do
   S := S + VariantValueForSQL(cds.Fields[I].Value) + ",";

 SQL.Add(....S...);
 cds.Next
end;


В датасете есть поле, содержащее Currency, соответственно, Value имеет тип varCurrency.

Функция VariantValueForSQL для этого поля выглядит так:

case VarType(Value) of
....
varCurrency,
   varSingle,
   varDouble  : Result := MoneyToStr(VarAsType(Value, varCurrency));
.....


Здесь MoneyToStr

function MoneyToStr(Value: Currency; const Format: string = "0.00##"): string;
begin
 Result := StringReplace(FormatCurr(Format, Value), DecimalSeparator, ".", []);
end;


А теперь вопрос. ИНОГДА эта фунция возвращает строку "-922337203685477.5808" для вполне безобидного числа вроде 1935,25.
VarType у значения в этот момент равен varCurrency, cds.Fields[I].AsString возвращает правильное число.
TVarData(cds.Fields[I].Value).RawData содержит правильные данные.

Но всё равно, в некоторых случаях число подменяется. Редко, но бывает. Повторить вручную не удаётся. Подмена DecimalSeparator в приложении не исключается (это моя текущая версия).

Интересует, в первую очередь, другое. Я в асме не силён, поэтому не смог досконально изучить функцию FloatToTextFmt, но есть подозрение, что именно она в каких-то случаях возвращает вместо переданного числа минимальное отрицательное число типа данных. Вопрос: в каких случаях это может происходить?


 
Inovet ©   (2012-07-26 09:31) [1]

> [0] Skyle ©   (26.07.12 08:28)
> -922337203685477.5808

Это $8000000000000000
и Currency это не float.


 
Skyle ©   (2012-07-26 09:43) [2]


> Inovet ©   (26.07.12 09:31) [1]
> > [0] Skyle ©   (26.07.12 08:28)
> > -922337203685477.5808
>
> Это $8000000000000000
> и Currency это не float.

Я в курсе, как это выглядит в hex. Про Currency и Float не понял.

Если речь идёт о том, почему я пишу о функции FloatToTextFmt, а у меня при этом Currency, то на этот вопрос могу ответить легко: потому что FormatCurr использует FloatToTextFmt.


 
RWolf ©   (2012-07-26 09:44) [3]

[1]

ТС это и написал:
> возвращает вместо переданного числа минимальное отрицательное число


 
Anatoly Podgoretsky ©   (2012-07-26 09:48) [4]


> Про Currency и Float не понял.

Чего тут понимать

varCurrency,
varSingle,
varDouble


 
Skyle ©   (2012-07-26 09:55) [5]


> Anatoly Podgoretsky ©   (26.07.12 09:48) [4]
>
> > Про Currency и Float не понял.
>
> Чего тут понимать
>
> varCurrency,
> varSingle,
> varDouble
>
>


Ну ладно, в данном случае это неважно. У нас везде varCurrency.

Я считаю, что проблема где-то в ассемблерном коде функции FloatToTextFmt.

Потому что VarAsType будет делать _VarCopy при передаче параметра в MoneyToStr и там не видно проблем (поэлементно копируется RawData). А вот что делается в асме - фиг знает. Плюс, есть подозрение, что именно ассемблерный код может одним махом превратить любое число в границу типа данных. Но вот что это за "мах" я пока не представляю.


 
Inovet ©   (2012-07-26 10:29) [6]

> [5] Skyle ©   (26.07.12 09:55)
> Плюс, есть подозрение, что именно ассемблерный код может
> одним махом превратить любое число в границу типа данных.

Ты же говоришь, что это не всегда происходит, как я понял даже одинаковыми значениями на самом входе. Во-первых, как-то сложно представить чтобы функция работала по разному на одних данных при тех же установках, DecimalSeparator тут тоже вряд ли влияет. Во-вторых, ты не можешь в момент возвращения такого неправильного числа посмотреть что там реально было. Вывод - ошибка скорее в другом месте. Может вставить для отладки условие проверки  на = $8000000000000000 перед вызовом?


 
Skyle ©   (2012-07-26 10:42) [7]

> Inovet ©   (26.07.12 10:29) [6]
Каждое утро приходят некие данные из подразделений. При их вставке генерируется этот самый SQL. Ошибка возникает тогда, когда вставка происходит подряд с нескольких точек. Одиночные вставки проходят всегда.
Вставка данных с нескольких точек происходит по очереди. Никаких параллельных вставок не бывает. Отправка данных вручную даёт ошибку только если я быстренько отправлю с нескольких точек сразу, чтобы создать очередь.

Я сохраняю в файлы содержимое TClientDataSet (через SaveToFile), отдельно сохраняю значение этого поля Fields[i].AsString. Сохраняю для этого поля в двоичный файл RawData от Fields[i].Value. Все эти файлы показывают верные значения. Отрицательный минимум появляется только иногда и только как результат функции VariantValueForSQL. Так что проблема видится где-то в ней. Возможно в MoneyToStr, FormatCurr, возможно во FloatToTextFmt.

Ошибка возникает примерно в одной строке на пять тысяч.


 
Inovet ©   (2012-07-26 11:15) [8]

> [7] Skyle ©   (26.07.12 10:42)
> Отрицательный минимум появляется только иногда и только
> как результат функции VariantValueForSQL. Так что проблема
> видится где-то в ней. Возможно в MoneyToStr, FormatCurr,
> возможно во FloatToTextFmt.

Перед вызовом сохраняй то, что в неё передаёшь или условие и сообщение вставь или в отладчмке условие.


 
Skyle ©   (2012-07-26 11:24) [9]

> Inovet ©   (26.07.12 11:15) [8]
Полностью согласен. Перед вызовом VariantValueForSQL я сохранял RawData, там всё нормально. Дальше, разумеется, придётся перетащить эту функцию в данный юнит и сохранять полученные параметры уже там.

Но дело в том, что проверка одной гипотезы занимает сутки (сегодня сделаю, завтра увижу результат). Поэтому написал на форум в надежде, что здесь добавят идей, почему подобное может происходить. Ведь число-то не самое простое, минимум типа данных.


 
Inovet ©   (2012-07-26 11:30) [10]

А у тебя и несколько Датасет в разных потоках?


 
Skyle ©   (2012-07-26 11:36) [11]

> Inovet ©   (26.07.12 11:30) [10]

Проведём аналогию.

type
 TDataSetImporter = class.....
 public
   procedure Import(...);
 end;


Экземпляр TDataSetImporter может быть в каждом потоке (их действительно несколько пытается вставиться, когда возникает ошибка). Но в метод Import они входят по очереди, ибо там блокировка на общем ресурсе (допустим, таблица в БД).


 
Inovet ©   (2012-07-26 11:48) [12]

> [11] Skyle ©   (26.07.12 11:36)
> Но в метод Import они входят по очереди, ибо там блокировка
> на общем ресурсе (допустим, таблица в БД).

Так до таблицы ещё много чего есть. А как на счёт разных Connection на каждый поток? И что в документации к БД и компонентам доступа говорится?


 
Anatoly Podgoretsky ©   (2012-07-26 11:48) [13]

Skyle ©   (26.07.12 08:28)  

Ты не приводишь информацию о функция в должном объеме, только function MoneyToStr(Value: Currency; const Format: string = "0.00##"): string;
приведена нормально. На хорошее обсуждение и помощь трудно в этом случае расчитывать.


 
Skyle ©   (2012-07-26 11:59) [14]


> Anatoly Podgoretsky ©   (26.07.12 11:48) [13]

Просто все остальные функции уже из SysUtils (начиная с FormatCurr, которая вызывается в MoneyToStr), не вижу смысла приводить их здесь.

Если поможет делу, то полный код функции VariantValueForSQL

function VariantValueForSQL(Value: Variant): String;
begin
 case VarType(Value) of
   varEmpty, varNull : Result := "NULL";
   varCurrency,
   varSingle,
   varDouble  : Result := MoneyToStr(VarAsType(Value, varCurrency));
   varDate : Result := DateToStrNull(VarAsType(Value, varDate));
   varSmallint,
   varInteger : Result := VarToString(Value);
   varBoolean : if Value then Result := "1" else Result := "0";
   else
     Result := QuotedStr(VarToString(Value));
 end;
end;


А больше здесь функций нет.


 
Skyle ©   (2012-07-26 12:00) [15]


> Inovet ©   (26.07.12 11:48) [12]
> > [11] Skyle ©   (26.07.12 11:36)
> > Но в метод Import они входят по очереди, ибо там блокировка
>
> > на общем ресурсе (допустим, таблица в БД).
>
> Так до таблицы ещё много чего есть. А как на счёт разных
> Connection на каждый поток? И что в документации к БД и
> компонентам доступа говорится?

До БД здесь не доходит. Сгенерированный SQL УЖЕ содержит ошибку. Да, код, описанный в классе генератора, вызывается в разных потоках в рамках своих экземпляров этого генератора. Но вместе они не работают, только по очереди.


 
Anatoly Podgoretsky ©   (2012-07-26 12:02) [16]

Не у всех есть Д2010


 
Skyle ©   (2012-07-26 12:16) [17]


> Anatoly Podgoretsky ©   (26.07.12 12:02) [16]
Не у всех есть Д2010

Справедливо. Пардоньте.

function FormatCurr(const Format: string; Value: Currency): string;
var
 Buffer: array[0..255] of Char;
begin
 if Length(Format) > Length(Buffer) - 32 then ConvertError(@SFormatTooLong);
 SetString(Result, Buffer, FloatToTextFmt(Buffer, Value, fvCurrency,
   PChar(Format)));
end;

function MoneyToStr(Value: Currency; const Format: string): string;
begin
 Result := StringReplace(FormatCurr(Format, Value), DecimalSeparator, ".", []);
end;

function VariantValueForSQL(Value: Variant): String;
begin
 case VarType(Value) of
   varEmpty, varNull : Result := "NULL";
   varCurrency,
   varSingle,
   varDouble  : Result := MoneyToStr(VarAsType(Value, varCurrency));
   varDate : Result := DateToStrNull(VarAsType(Value, varDate));
   varSmallint,
   varInteger : Result := VarToString(Value);
   varBoolean : if Value then Result := "1" else Result := "0";
   else
     Result := QuotedStr(VarToString(Value));
 end;
end;

function DeleteLastChar(AValue: String): String;
var
 L: Integer;
begin
 L := Length(AValue);
 if L > 0 then
   System.Delete(AValue, L, 1);
 Result := AValue;
end;

procedure Import;
var
 SS, Appendix, Header : String;
 SL : TStrings;
 I: Integer;
begin
.....
             while not Eof do
             begin
               SS := Appendix;

               for I := 0 to Pred(FieldDefs.Count) do
                 SS := SS + VariantValueForSQL(Fields[I].Value) + ",";
               SS  := DeleteLastChar(SS);

               SQL := Header + Format("VALUES (%s)", [SS]);
   
               SL.Add(SQL);
               Next;
             end;



Сама функция FloatToTextFmt здоровая, поэтому выложил её сюда
http://pastebin.com/3FkkLT7K



Страницы: 1 вся ветка

Текущий архив: 2017.01.15;
Скачать: CL | DM;

Наверх




Память: 0.53 MB
Время: 0.027 c
2-1424288600
AndrewAndrey
2015-02-18 22:43
2017.01.15
Связь полей таблиц SQLite


15-1457040604
Юрий
2016-03-04 00:30
2017.01.15
С днем рождения ! 4 марта 2016 пятница


15-1455467104
Bjdob
2016-02-14 19:25
2017.01.15
Исторические справочники


15-1454505647
кгшзх
2016-02-03 16:20
2017.01.15
ПДД баттл


1-1345382325
abolnykh
2012-08-19 17:18
2017.01.15
Импорт объекта из dll