2 // System.Globalization.NumberFormatInfo.cs
5 // Derek Holden (dholden@draper.com)
6 // Bob Smith (bob@thestuff.net)
7 // Mohammad DAMT (mdamt@cdl2000.com)
10 // (C) Bob Smith http://www.thestuff.net
11 // (c) 2003, PT Cakram Datalingga Duaribu http://www.cdl2000.com
15 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 // NumberFormatInfo. One can only assume it is the class gotten
39 // back from a GetFormat() method from an IFormatProvider /
40 // IFormattable implementer. There are some discrepencies with the
41 // ECMA spec and the SDK docs, surprisingly. See my conversation
42 // with myself on it at:
43 // http://lists.ximian.com/archives/public/mono-list/2001-July/000794.html
45 // Other than that this is totally ECMA compliant.
48 using System
.Runtime
.InteropServices
;
50 namespace System
.Globalization
{
54 public sealed class NumberFormatInfo
: ICloneable
, IFormatProvider
{
56 /* Keep in sync with object-internals.h */
58 #pragma warning disable 649
59 private bool isReadOnly
;
60 // used for temporary storage. Used in InitPatterns ()
61 string decimalFormats
;
62 string currencyFormats
;
63 string percentFormats
;
64 string digitPattern
= "#";
65 string zeroPattern
= "0";
67 // Currency Related Format Info
68 private int currencyDecimalDigits
;
69 private string currencyDecimalSeparator
;
70 private string currencyGroupSeparator
;
71 private int[] currencyGroupSizes
;
72 private int currencyNegativePattern
;
73 private int currencyPositivePattern
;
74 private string currencySymbol
;
76 private string nanSymbol
;
77 private string negativeInfinitySymbol
;
78 private string negativeSign
;
80 // Number Related Format Info
81 private int numberDecimalDigits
;
82 private string numberDecimalSeparator
;
83 private string numberGroupSeparator
;
84 private int[] numberGroupSizes
;
85 private int numberNegativePattern
;
87 // Percent Related Format Info
88 private int percentDecimalDigits
;
89 private string percentDecimalSeparator
;
90 private string percentGroupSeparator
;
91 private int[] percentGroupSizes
;
92 private int percentNegativePattern
;
93 private int percentPositivePattern
;
94 private string percentSymbol
;
96 private string perMilleSymbol
;
97 private string positiveInfinitySymbol
;
98 private string positiveSign
;
99 #pragma warning restore 649
101 #pragma warning disable 169
102 string ansiCurrencySymbol
; // TODO, MS.NET serializes this.
103 int m_dataItem
; // Unused, but MS.NET serializes this.
104 bool m_useUserOverride
; // Unused, but MS.NET serializes this.
105 bool validForParseAsNumber
; // Unused, but MS.NET serializes this.
106 bool validForParseAsCurrency
; // Unused, but MS.NET serializes this.
107 #pragma warning restore 169
109 string[] nativeDigits
= invariantNativeDigits
;
110 int digitSubstitution
= 1; // DigitShapes.None.
112 static readonly string [] invariantNativeDigits
= new string [] {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
;
114 internal NumberFormatInfo (int lcid
, bool read_only
)
116 isReadOnly
= read_only
;
118 //FIXME: should add more LCID
119 // CultureInfo uses this one also.
125 // The Invariant Culture Info ID.
129 // Currency Related Format Info
130 currencyDecimalDigits
= 2;
131 currencyDecimalSeparator
= ".";
132 currencyGroupSeparator
= ",";
133 currencyGroupSizes
= new int[1] { 3 }
;
134 currencyNegativePattern
= 0;
135 currencyPositivePattern
= 0;
136 currencySymbol
= "$";
139 negativeInfinitySymbol
= "-Infinity";
142 // Number Related Format Info
143 numberDecimalDigits
= 2;
144 numberDecimalSeparator
= ".";
145 numberGroupSeparator
= ",";
146 numberGroupSizes
= new int[1] { 3 }
;
147 numberNegativePattern
= 1;
149 // Percent Related Format Info
150 percentDecimalDigits
= 2;
151 percentDecimalSeparator
= ".";
152 percentGroupSeparator
= ",";
153 percentGroupSizes
= new int[1] { 3 }
;
154 percentNegativePattern
= 0;
155 percentPositivePattern
= 0;
158 perMilleSymbol
= "\u2030";
159 positiveInfinitySymbol
= "Infinity";
165 internal NumberFormatInfo (bool read_only
) : this (0x007f, read_only
)
169 public NumberFormatInfo () : this (false)
173 // this is called by mono/mono/metadata/locales.c
174 #pragma warning disable 169
177 string [] partOne
, partTwo
;
178 string [] posNeg
= decimalFormats
.Split (new char [1] {';'}
, 2);
180 if (posNeg
.Length
== 2) {
182 partOne
= posNeg
[0].Split (new char [1] {'.'}
, 2);
184 if (partOne
.Length
== 2) {
185 // assumed same for both positive and negative
186 // decimal digit side
187 numberDecimalDigits
= 0;
188 for (int i
= 0; i
< partOne
[1].Length
; i
++) {
189 if (partOne
[1][i
] == digitPattern
[0]) {
190 numberDecimalDigits
++;
195 // decimal grouping side
196 partTwo
= partOne
[0].Split (',');
197 if (partTwo
.Length
> 1) {
198 numberGroupSizes
= new int [partTwo
.Length
- 1];
199 for (int i
= 0; i
< numberGroupSizes
.Length
; i
++) {
200 string pat
= partTwo
[i
+ 1];
201 numberGroupSizes
[i
] = pat
.Length
;
204 numberGroupSizes
= new int [1] { 0 }
;
207 if (posNeg
[1].StartsWith ("(") && posNeg
[1].EndsWith (")")) {
208 numberNegativePattern
= 0;
209 } else if (posNeg
[1].StartsWith ("- ")) {
210 numberNegativePattern
= 2;
211 } else if (posNeg
[1].StartsWith ("-")) {
212 numberNegativePattern
= 1;
213 } else if (posNeg
[1].EndsWith (" -")) {
214 numberNegativePattern
= 4;
215 } else if (posNeg
[1].EndsWith ("-")) {
216 numberNegativePattern
= 3;
218 numberNegativePattern
= 1;
223 posNeg
= currencyFormats
.Split (new char [1] {';'}
, 2);
224 if (posNeg
.Length
== 2) {
225 partOne
= posNeg
[0].Split (new char [1] {'.'}
, 2);
227 if (partOne
.Length
== 2) {
228 // assumed same for both positive and negative
229 // decimal digit side
230 currencyDecimalDigits
= 0;
231 for (int i
= 0; i
< partOne
[1].Length
; i
++) {
232 if (partOne
[1][i
] == zeroPattern
[0])
233 currencyDecimalDigits
++;
238 // decimal grouping side
239 partTwo
= partOne
[0].Split (',');
240 if (partTwo
.Length
> 1) {
241 currencyGroupSizes
= new int [partTwo
.Length
- 1];
242 for (int i
= 0; i
< currencyGroupSizes
.Length
; i
++) {
243 string pat
= partTwo
[i
+ 1];
244 currencyGroupSizes
[i
] = pat
.Length
;
247 currencyGroupSizes
= new int [1] { 0 }
;
250 if (posNeg
[1].StartsWith ("(\u00a4 ") && posNeg
[1].EndsWith (")")) {
251 currencyNegativePattern
= 14;
252 } else if (posNeg
[1].StartsWith ("(\u00a4") && posNeg
[1].EndsWith (")")) {
253 currencyNegativePattern
= 0;
254 } else if (posNeg
[1].StartsWith ("\u00a4 ") && posNeg
[1].EndsWith ("-")) {
255 currencyNegativePattern
= 11;
256 } else if (posNeg
[1].StartsWith ("\u00a4") && posNeg
[1].EndsWith ("-")) {
257 currencyNegativePattern
= 3;
258 } else if (posNeg
[1].StartsWith ("(") && posNeg
[1].EndsWith (" \u00a4")) {
259 currencyNegativePattern
= 15;
260 } else if (posNeg
[1].StartsWith ("(") && posNeg
[1].EndsWith ("\u00a4")) {
261 currencyNegativePattern
= 4;
262 } else if (posNeg
[1].StartsWith ("-") && posNeg
[1].EndsWith (" \u00a4")) {
263 currencyNegativePattern
= 8;
264 } else if (posNeg
[1].StartsWith ("-") && posNeg
[1].EndsWith ("\u00a4")) {
265 currencyNegativePattern
= 5;
266 } else if (posNeg
[1].StartsWith ("-\u00a4 ")) {
267 currencyNegativePattern
= 9;
268 } else if (posNeg
[1].StartsWith ("-\u00a4")) {
269 currencyNegativePattern
= 1;
270 } else if (posNeg
[1].StartsWith ("\u00a4 -")) {
271 currencyNegativePattern
= 12;
272 } else if (posNeg
[1].StartsWith ("\u00a4-")) {
273 currencyNegativePattern
= 2;
274 } else if (posNeg
[1].EndsWith (" \u00a4-")) {
275 currencyNegativePattern
= 10;
276 } else if (posNeg
[1].EndsWith ("\u00a4-")) {
277 currencyNegativePattern
= 7;
278 } else if (posNeg
[1].EndsWith ("- \u00a4")) {
279 currencyNegativePattern
= 13;
280 } else if (posNeg
[1].EndsWith ("-\u00a4")) {
281 currencyNegativePattern
= 6;
283 currencyNegativePattern
= 0;
286 if (posNeg
[0].StartsWith ("\u00a4 ")) {
287 currencyPositivePattern
= 2;
288 } else if (posNeg
[0].StartsWith ("\u00a4")) {
289 currencyPositivePattern
= 0;
290 } else if (posNeg
[0].EndsWith (" \u00a4")) {
291 currencyPositivePattern
= 3;
292 } else if (posNeg
[0].EndsWith ("\u00a4")) {
293 currencyPositivePattern
= 1;
295 currencyPositivePattern
= 0;
300 // we don't have percentNegativePattern in CLDR so
301 // the percentNegativePattern are just guesses
302 if (percentFormats
.StartsWith ("%")) {
303 percentPositivePattern
= 2;
304 percentNegativePattern
= 2;
305 } else if (percentFormats
.EndsWith (" %")) {
306 percentPositivePattern
= 0;
307 percentNegativePattern
= 0;
308 } else if (percentFormats
.EndsWith ("%")) {
309 percentPositivePattern
= 1;
310 percentNegativePattern
= 1;
312 percentPositivePattern
= 0;
313 percentNegativePattern
= 0;
316 partOne
= percentFormats
.Split (new char [1] {'.'}
, 2);
318 if (partOne
.Length
== 2) {
319 // assumed same for both positive and negative
320 // decimal digit side
321 percentDecimalDigits
= 0;
322 for (int i
= 0; i
< partOne
[1].Length
; i
++) {
323 if (partOne
[1][i
] == digitPattern
[0])
324 percentDecimalDigits
++;
329 // percent grouping side
330 partTwo
= partOne
[0].Split (',');
331 if (partTwo
.Length
> 1) {
332 percentGroupSizes
= new int [partTwo
.Length
- 1];
333 for (int i
= 0; i
< percentGroupSizes
.Length
; i
++) {
334 string pat
= partTwo
[i
+ 1];
335 percentGroupSizes
[i
] = pat
.Length
;
338 percentGroupSizes
= new int [1] { 0 }
;
343 #pragma warning restore 169
345 // =========== Currency Format Properties =========== //
347 public int CurrencyDecimalDigits
{
349 return currencyDecimalDigits
;
353 if (value < 0 || value > 99)
354 throw new ArgumentOutOfRangeException
355 ("The value specified for the property is less than 0 or greater than 99");
358 throw new InvalidOperationException
359 ("The current instance is read-only and a set operation was attempted");
361 currencyDecimalDigits
= value;
365 public string CurrencyDecimalSeparator
{
367 return currencyDecimalSeparator
;
372 throw new ArgumentNullException
373 ("The value specified for the property is a null reference");
376 throw new InvalidOperationException
377 ("The current instance is read-only and a set operation was attempted");
379 currencyDecimalSeparator
= value;
384 public string CurrencyGroupSeparator
{
386 return currencyGroupSeparator
;
391 throw new ArgumentNullException
392 ("The value specified for the property is a null reference");
395 throw new InvalidOperationException
396 ("The current instance is read-only and a set operation was attempted");
398 currencyGroupSeparator
= value;
402 public int[] CurrencyGroupSizes
{
404 return (int []) RawCurrencyGroupSizes
.Clone ();
408 RawCurrencyGroupSizes
= value;
412 internal int[] RawCurrencyGroupSizes
{
414 return currencyGroupSizes
;
419 throw new ArgumentNullException
420 ("The value specified for the property is a null reference");
423 throw new InvalidOperationException
424 ("The current instance is read-only and a set operation was attempted");
426 if (value.Length
== 0) {
427 currencyGroupSizes
= new int [0];
431 // All elements except last need to be in range 1 - 9, last can be 0.
432 int last
= value.Length
- 1;
434 for (int i
= 0; i
< last
; i
++)
435 if (value[i
] < 1 || value[i
] > 9)
436 throw new ArgumentOutOfRangeException
437 ("One of the elements in the array specified is not between 1 and 9");
439 if (value[last
] < 0 || value[last
] > 9)
440 throw new ArgumentOutOfRangeException
441 ("Last element in the array specified is not between 0 and 9");
443 currencyGroupSizes
= (int[]) value.Clone();
447 public int CurrencyNegativePattern
{
449 // See ECMA NumberFormatInfo page 8
450 return currencyNegativePattern
;
454 if (value < 0 || value > 15)
455 throw new ArgumentOutOfRangeException
456 ("The value specified for the property is less than 0 or greater than 15");
459 throw new InvalidOperationException
460 ("The current instance is read-only and a set operation was attempted");
462 currencyNegativePattern
= value;
466 public int CurrencyPositivePattern
{
468 // See ECMA NumberFormatInfo page 11
469 return currencyPositivePattern
;
473 if (value < 0 || value > 3)
474 throw new ArgumentOutOfRangeException
475 ("The value specified for the property is less than 0 or greater than 3");
478 throw new InvalidOperationException
479 ("The current instance is read-only and a set operation was attempted");
481 currencyPositivePattern
= value;
485 public string CurrencySymbol
{
487 return currencySymbol
;
492 throw new ArgumentNullException
493 ("The value specified for the property is a null reference");
496 throw new InvalidOperationException
497 ("The current instance is read-only and a set operation was attempted");
499 currencySymbol
= value;
503 // =========== Static Read-Only Properties =========== //
505 public static NumberFormatInfo CurrentInfo
{
507 NumberFormatInfo nfi
= (NumberFormatInfo
) System
.Threading
.Thread
.CurrentThread
.CurrentCulture
.NumberFormat
;
508 nfi
.isReadOnly
= true;
513 public static NumberFormatInfo InvariantInfo
{
515 // This uses invariant info, which is same as in the constructor
516 NumberFormatInfo nfi
= new NumberFormatInfo ();
517 nfi
.NumberNegativePattern
= 1;
518 nfi
.isReadOnly
= true;
523 public bool IsReadOnly
{
531 public string NaNSymbol
{
538 throw new ArgumentNullException
539 ("The value specified for the property is a null reference");
542 throw new InvalidOperationException
543 ("The current instance is read-only and a set operation was attempted");
550 [MonoNotSupported ("We don't have native digit info")]
552 public string [] NativeDigits
{
553 get { return nativeDigits; }
556 throw new ArgumentNullException ("value");
557 if (value.Length
!= 10)
558 throw new ArgumentException ("Argument array length must be 10");
559 foreach (string s
in value)
560 if (String
.IsNullOrEmpty (s
))
561 throw new ArgumentException ("Argument array contains one or more null strings");
562 nativeDigits
= value;
566 [MonoNotSupported ("We don't have native digit info")]
568 public DigitShapes DigitSubstitution
{
569 get { return (DigitShapes) digitSubstitution; }
570 set { digitSubstitution = (int) value; }
574 public string NegativeInfinitySymbol
{
576 return negativeInfinitySymbol
;
581 throw new ArgumentNullException
582 ("The value specified for the property is a null reference");
585 throw new InvalidOperationException
586 ("The current instance is read-only and a set operation was attempted");
588 negativeInfinitySymbol
= value;
592 public string NegativeSign
{
599 throw new ArgumentNullException
600 ("The value specified for the property is a null reference");
603 throw new InvalidOperationException
604 ("The current instance is read-only and a set operation was attempted");
606 negativeSign
= value;
610 // =========== Number Format Properties =========== //
612 public int NumberDecimalDigits
{
614 return numberDecimalDigits
;
618 if (value < 0 || value > 99)
619 throw new ArgumentOutOfRangeException
620 ("The value specified for the property is less than 0 or greater than 99");
623 throw new InvalidOperationException
624 ("The current instance is read-only and a set operation was attempted");
626 numberDecimalDigits
= value;
630 public string NumberDecimalSeparator
{
632 return numberDecimalSeparator
;
637 throw new ArgumentNullException
638 ("The value specified for the property is a null reference");
641 throw new InvalidOperationException
642 ("The current instance is read-only and a set operation was attempted");
644 numberDecimalSeparator
= value;
649 public string NumberGroupSeparator
{
651 return numberGroupSeparator
;
656 throw new ArgumentNullException
657 ("The value specified for the property is a null reference");
660 throw new InvalidOperationException
661 ("The current instance is read-only and a set operation was attempted");
663 numberGroupSeparator
= value;
667 public int[] NumberGroupSizes
{
669 return (int []) RawNumberGroupSizes
.Clone ();
673 RawNumberGroupSizes
= value;
677 internal int[] RawNumberGroupSizes
{
679 return numberGroupSizes
;
684 throw new ArgumentNullException
685 ("The value specified for the property is a null reference");
688 throw new InvalidOperationException
689 ("The current instance is read-only and a set operation was attempted");
691 if (value.Length
== 0) {
692 numberGroupSizes
= new int [0];
695 // All elements except last need to be in range 1 - 9, last can be 0.
696 int last
= value.Length
- 1;
698 for (int i
= 0; i
< last
; i
++)
699 if (value[i
] < 1 || value[i
] > 9)
700 throw new ArgumentOutOfRangeException
701 ("One of the elements in the array specified is not between 1 and 9");
703 if (value[last
] < 0 || value[last
] > 9)
704 throw new ArgumentOutOfRangeException
705 ("Last element in the array specified is not between 0 and 9");
707 numberGroupSizes
= (int[]) value.Clone();
711 public int NumberNegativePattern
{
713 // See ECMA NumberFormatInfo page 27
714 return numberNegativePattern
;
718 if (value < 0 || value > 4)
719 throw new ArgumentOutOfRangeException
720 ("The value specified for the property is less than 0 or greater than 15");
723 throw new InvalidOperationException
724 ("The current instance is read-only and a set operation was attempted");
726 numberNegativePattern
= value;
730 // =========== Percent Format Properties =========== //
732 public int PercentDecimalDigits
{
734 return percentDecimalDigits
;
738 if (value < 0 || value > 99)
739 throw new ArgumentOutOfRangeException
740 ("The value specified for the property is less than 0 or greater than 99");
743 throw new InvalidOperationException
744 ("The current instance is read-only and a set operation was attempted");
746 percentDecimalDigits
= value;
750 public string PercentDecimalSeparator
{
752 return percentDecimalSeparator
;
757 throw new ArgumentNullException
758 ("The value specified for the property is a null reference");
761 throw new InvalidOperationException
762 ("The current instance is read-only and a set operation was attempted");
764 percentDecimalSeparator
= value;
769 public string PercentGroupSeparator
{
771 return percentGroupSeparator
;
776 throw new ArgumentNullException
777 ("The value specified for the property is a null reference");
780 throw new InvalidOperationException
781 ("The current instance is read-only and a set operation was attempted");
783 percentGroupSeparator
= value;
787 public int[] PercentGroupSizes
{
789 return (int []) RawPercentGroupSizes
.Clone ();
793 RawPercentGroupSizes
= value;
797 internal int[] RawPercentGroupSizes
{
799 return percentGroupSizes
;
804 throw new ArgumentNullException
805 ("The value specified for the property is a null reference");
808 throw new InvalidOperationException
809 ("The current instance is read-only and a set operation was attempted");
811 if (this == CultureInfo
.CurrentCulture
.NumberFormat
)
812 throw new Exception ("HERE the value was modified");
814 if (value.Length
== 0) {
815 percentGroupSizes
= new int [0];
819 // All elements except last need to be in range 1 - 9, last can be 0.
820 int last
= value.Length
- 1;
822 for (int i
= 0; i
< last
; i
++)
823 if (value[i
] < 1 || value[i
] > 9)
824 throw new ArgumentOutOfRangeException
825 ("One of the elements in the array specified is not between 1 and 9");
827 if (value[last
] < 0 || value[last
] > 9)
828 throw new ArgumentOutOfRangeException
829 ("Last element in the array specified is not between 0 and 9");
831 percentGroupSizes
= (int[]) value.Clone();
835 public int PercentNegativePattern
{
837 // See ECMA NumberFormatInfo page 8
838 return percentNegativePattern
;
842 if (value < 0 || value > 2)
843 throw new ArgumentOutOfRangeException
844 ("The value specified for the property is less than 0 or greater than 15");
847 throw new InvalidOperationException
848 ("The current instance is read-only and a set operation was attempted");
850 percentNegativePattern
= value;
854 public int PercentPositivePattern
{
856 // See ECMA NumberFormatInfo page 11
857 return percentPositivePattern
;
861 if (value < 0 || value > 2)
862 throw new ArgumentOutOfRangeException
863 ("The value specified for the property is less than 0 or greater than 3");
866 throw new InvalidOperationException
867 ("The current instance is read-only and a set operation was attempted");
869 percentPositivePattern
= value;
873 public string PercentSymbol
{
875 return percentSymbol
;
880 throw new ArgumentNullException
881 ("The value specified for the property is a null reference");
884 throw new InvalidOperationException
885 ("The current instance is read-only and a set operation was attempted");
887 percentSymbol
= value;
891 public string PerMilleSymbol
{
893 return perMilleSymbol
;
898 throw new ArgumentNullException
899 ("The value specified for the property is a null reference");
902 throw new InvalidOperationException
903 ("The current instance is read-only and a set operation was attempted");
905 perMilleSymbol
= value;
909 public string PositiveInfinitySymbol
{
911 return positiveInfinitySymbol
;
916 throw new ArgumentNullException
917 ("The value specified for the property is a null reference");
920 throw new InvalidOperationException
921 ("The current instance is read-only and a set operation was attempted");
923 positiveInfinitySymbol
= value;
927 public string PositiveSign
{
934 throw new ArgumentNullException
935 ("The value specified for the property is a null reference");
938 throw new InvalidOperationException
939 ("The current instance is read-only and a set operation was attempted");
941 positiveSign
= value;
945 public object GetFormat (Type formatType
)
947 return (formatType
== typeof (NumberFormatInfo
)) ? this : null;
950 public object Clone ()
952 NumberFormatInfo clone
= (NumberFormatInfo
) MemberwiseClone();
953 // clone is not read only
954 clone
.isReadOnly
= false;
958 public static NumberFormatInfo
ReadOnly (NumberFormatInfo nfi
)
960 NumberFormatInfo copy
= (NumberFormatInfo
)nfi
.Clone();
961 copy
.isReadOnly
= true;
965 public static NumberFormatInfo
GetInstance(IFormatProvider formatProvider
)
967 if (formatProvider
!= null) {
968 NumberFormatInfo nfi
;
969 nfi
= (NumberFormatInfo
)formatProvider
.GetFormat(typeof(NumberFormatInfo
));