Ответ 1
Delphi предоставляет процедуру под названием FloatToDecimal
, которая преобразует значения с плавающей точкой (например, Extended
) и Currency
в полезную структуру для дальнейшего форматирования. например:.
FloatToDecimal(..., 1234567890.1234, ...);
дает вам:
TFloatRec
Digits: array[0..20] of Char = "12345678901234"
Exponent: SmallInt = 10
IsNegative: Boolean = True
Где Exponent
задает число цифр слева от десятичной точки.
Есть несколько специальных случаев, которые нужно обрабатывать:
-
Экспонента равна нулю
Digits: array[0..20] of Char = "12345678901234" Exponent: SmallInt = 0 IsNegative: Boolean = True
означает, что нет цифр слева от десятичной точки, например.
.12345678901234
-
Экспонента отрицательна
Digits: array[0..20] of Char = "12345678901234" Exponent: SmallInt = -3 IsNegative: Boolean = True
означает, что вы должны помещать нули между десятичной точкой и первой цифрой, например.
.00012345678901234
-
Экспонента
-32768
( NaN, а не число)Digits: array[0..20] of Char = "" Exponent: SmallInt = -32768 IsNegative: Boolean = False
означает, что значение не является числом, например.
NAN
-
Экспонента
32767
(INF или -INF)Digits: array[0..20] of Char = "" Exponent: SmallInt = 32767 IsNegative: Boolean = False
означает, что значение представляет собой либо положительную, либо отрицательную бесконечность (в зависимости от значения
IsNegative
), например.-INF
Мы можем использовать FloatToDecimal
в качестве отправной точки для создания независимой от языка строки "кодов изображений".
Затем эта строка может быть передана соответствующим функциям Windows GetNumberFormat
или GetCurrencyFormat
для выполнения правильной локализации.
Я написал свои собственные CurrToDecimalString
и FloatToDecimalString
, которые преобразуют числа в требуемый независимый от локали формат:
class function TGlobalization.CurrToDecimalString(const Value: Currency): string;
var
digits: string;
s: string;
floatRec: TFloatRec;
begin
FloatToDecimal({var}floatRec, Value, fvCurrency, 0{ignored for currency types}, 9999);
//convert the array of char into an easy to access string
digits := PChar(Addr(floatRec.Digits[0]));
if floatRec.Exponent > 0 then
begin
//Check for positive or negative infinity (exponent = 32767)
if floatRec.Exponent = 32767 then //David Heffernan says that currency can never be infinity. Even though i can't test it, i can at least try to handle it
begin
if floatRec.Negative = False then
Result := 'INF'
else
Result := '-INF';
Exit;
end;
{
digits: 1234567 89
exponent--------^ 7=7 digits on left of decimal mark
}
s := Copy(digits, 1, floatRec.Exponent);
{
for the value 10000:
digits: "1"
exponent: 5
Add enough zero to digits to pad it out to exponent digits
}
if Length(s) < floatRec.Exponent then
s := s+StringOfChar('0', floatRec.Exponent-Length(s));
if Length(digits) > floatRec.Exponent then
s := s+'.'+Copy(digits, floatRec.Exponent+1, 20);
end
else if floatRec.Exponent < 0 then
begin
//check for NaN (Exponent = -32768)
if floatRec.Exponent = -32768 then //David Heffernan says that currency can never be NotANumber. Even though i can't test it, i can at least try to handle it
begin
Result := 'NAN';
Exit;
end;
{
digits: .000123456789
^---------exponent
}
//Add zero, or more, "0" to the left
s := '0.'+StringOfChar('0', -floatRec.Exponent)+digits;
end
else
begin
{
Exponent is zero.
digits: .123456789
^
}
if length(digits) > 0 then
s := '0.'+digits
else
s := '0';
end;
if floatRec.Negative then
s := '-'+s;
Result := s;
end;
Помимо краевых случаев NAN
, INF
и -INF
, я могу передать эти строки в Windows:
class function TGlobalization.GetCurrencyFormat(const DecimalString: WideString; const Locale: LCID): WideString;
var
cch: Integer;
ValueStr: WideString;
begin
Locale
LOCALE_INVARIANT
LOCALE_USER_DEFAULT <--- use this one (windows.pas)
LOCALE_SYSTEM_DEFAULT
LOCALE_CUSTOM_DEFAULT (Vista and later)
LOCALE_CUSTOM_UI_DEFAULT (Vista and later)
LOCALE_CUSTOM_UNSPECIFIED (Vista and later)
}
cch := Windows.GetCurrencyFormatW(Locale, 0, PWideChar(DecimalString), nil, nil, 0);
if cch = 0 then
RaiseLastWin32Error;
SetLength(ValueStr, cch);
cch := Windows.GetCurrencyFormatW(Locale, 0, PWideChar(DecimalString), nil, PWideChar(ValueStr), Length(ValueStr));
if (cch = 0) then
RaiseLastWin32Error;
SetLength(ValueStr, cch-1); //they include the null terminator /facepalm
Result := ValueStr;
end;
Реализации
FloatToDecimalString
иGetNumberFormat
оставлены как упражнение для читателя (так как я на самом деле еще не написал float-one, просто валюту - я не знаю, как я буду обрабатывать экспоненциальные обозначения).
И Боб, дядя; правильно локализованные поплавки и валюты в Delphi.
i уже прошел работу по правильной локализации целых чисел, дат, времени и времени.
Примечание. Любой код выпущен в общедоступном домене. Не требуется атрибуция.