Форум: "Основная";
Текущий архив: 2017.01.15;
Скачать: [xml.tar.bz2];
Вниз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), не вижу смысла приводить их здесь.
Если поможет делу, то полный код функции VariantValueForSQLfunction 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;
Скачать: [xml.tar.bz2];
Память: 0.51 MB
Время: 0.099 c