Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 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), не вижу смысла приводить их здесь.

Если поможет делу, то полный код функции 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;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.51 MB
Время: 0.05 c
15-1453995709
Внук
2016-01-28 18:41
2017.01.15
Классовые методы с неклассовыми свойствами


15-1453057828
Кто б сомневался
2016-01-17 22:10
2017.01.15
raise exception до Application.Run НЕ покажет сообщение.


15-1457536558
DVM
2016-03-09 18:15
2017.01.15
Как думаете, это ошибка в TPointerStream в VCL?


2-1422788793
A1ekceu
2015-02-01 14:06
2017.01.15
Проверка кода


2-1427465762
Token
2015-03-27 17:16
2017.01.15
Как задать имя файла компиляции





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский