Delphi FormatFloat другое поведение (переменная и набор данных)
Я обнаружил странное поведение функции FormatFloat в Delphi. Позвольте мне показать тематическое исследование.
значение для преобразования: 129809,495
желаемый форматированный вывод: 129 809,50
Случай 1: преобразование из строки
var str: string;
str := '129809.495';
str := FormatFloat(',0.00', StrToFloat(str));
// output is 129,809.50 = CORRECT
Случай 2: преобразование из двойной переменной
var number: double;
str: string;
val := 129809.495;
str := FormatFloat(',0.00', val);
// output is 129,809.50 = CORRECT
Случай 3: Преобразование из поля набора данных
*it's too complex to write here, let me just explain*
Basically the formatted output of FormatFloat(',0.00', Dataset.Field[0].AsFloat);
always resulted in 129,809.49 == WRONG
Я тестировал это поведение с использованием SQL Server 2008 и Firebird 1.5.
Используемые компоненты - это ADO-компоненты и UniDAC-компоненты (от DevArt), и все они ведут себя одинаково.
Я пытался сделать это:
- FormatFloat (', 0.00', Dataset.Field [0].AsFloat);
- FormatFloat (', 0,00', StrToFloat (Dataset.Field [0].AsString));
- val: = Dataset.Field [0].AsFloat; FormatFloat (', 0,00', val);
- str: = Dataset.Field [0].AsString; FormatFloat (', 0,00', StrToFloat (str));
- val: = StrToFloat (Dataset.Field [0].AsString); FormatFloat (', 0,00', val);
Но все это привело к тому же неправильному преобразованию.49 вместо.50
1 способ, который работает
val := StrToFloat(Dataset.Field[0].AsString);
FormatFloat(',0.00', val);
У кого-нибудь есть решение для такого поведения? Потому что было бы слишком много работы, чтобы принудительно преобразовать StrToFloat и затем переформатировать переменную / вывод. И этот обходной путь не может быть применен к сторонним компонентам, которые использовали FormatFloat
Любая помощь приветствуется. Спасибо
2 ответа
Похоже, это связано с некоторыми различиями в точности между Double и Extended. На самом деле я не могу подтвердить ваше замечание о том, что дело 2 верно. Возможно, потому что вы объявляете переменную number как double, но затем используете переменную val неизвестного типа.
Во всяком случае, следующий код
var
d: Double;
e: Extended;
begin
d := 129809.495;
e := 129809.495;
Writeln(FormatFloat(',0.00', d));
Writeln(FormatFloat(',0.00', e));
e := d;
Writeln(FormatFloat(',0.00', e));
e := 129809.495;
d := e;
Writeln(FormatFloat(',0.00', d));
end;
скомпилированный с XE6 выдает следующий вывод:
129,809.49
129,809.50
129,809.49
129,809.49
Это приводит к заключению, что удвоение не может содержать правильное значение для округления, как ожидается, в то время как расширенный является более подходящим. Кроме того, когда значение однажды сохраняется в двойном формате, простое преобразование его в расширенное (это то, что происходит с FormatFloat) не лечит ошибку округления.
Сделайте модуль MyUnit вот таким:
unit MyUnit;
interface
uses
System.SysUtils;
function FormatFloat(const Format: string; Value: Extended): string;
implementation
function FormatFloat(const Format: string; Value: Extended): string;
begin
Value := StrToFloat(Value.ToString);
Result:=System.SysUtils.FormatFloat(',0.00', Value);
end;
end.
и вставьте его на последнем месте предложения Uses. Любой вызов функции FormatFloat вызовет вашу функцию.
Кстати, такое поведение может быть результатом округления или недостаточной точности. Я попробовал это:
var
val: Single;
begin
val:=129809.495;
ShowMessage(FormatFloat(',0.00', val));
с моей функцией и rseult был val 129809.49, потому что переменная, переданная в функцию, была 129809.4921875 после преобразования из Single в Extended. Поэтому для отладки записи в файл журнала каждого вызова функции может быть хорошей идеей.