@@ -282,238 +282,27 @@ internal static class Number
282282 {
283283 public static String Format ( Object value , bool isInteger , String format , NumberFormatInfo info )
284284 {
285- char formatCh ;
286- int precision ;
287- ValidateFormat ( format , out formatCh , out precision ) ;
288-
289- String result = FormatNative ( value , formatCh , precision ) ;
290-
291- if ( isInteger )
292- {
293- // Special case : type is integer, value = 0 and format string = "N0"
294- // No processing needed, simply return "0" (native code returns empty string for this special case)
295- if ( isInteger && result == String . Empty && format == "N0" ) return "0" ;
296-
297- return PostProcessInteger ( value , result , formatCh , precision , info ) ;
298- }
299- return PostProcessFloat ( result , formatCh , precision , info ) ;
285+ String ret = FormatNative (
286+ value ,
287+ isInteger ,
288+ format ,
289+ info . NumberDecimalSeparator ,
290+ info . NegativeSign ,
291+ info . NumberGroupSeparator ,
292+ info . NumberGroupSizes ) ;
293+
294+ return ret ;
300295 }
301296
302297 [ MethodImpl ( MethodImplOptions . InternalCall ) ]
303- private static extern String FormatNative ( Object value , char format , int precision ) ;
304-
305- private static void ValidateFormat ( String format , out char formatCh , out int precision )
306- {
307- precision = 0 ;
308-
309- if ( format == null || format == "" )
310- {
311- formatCh = 'G' ;
312- return ;
313- }
314-
315- formatCh = format [ 0 ] ;
316-
317- // ToUpper, since all the supported format characters are invariant in case
318- if ( formatCh >= 'a' && formatCh <= 'z' )
319- {
320- formatCh = ( char ) ( formatCh - ( 'a' - 'A' ) ) ;
321- }
322-
323- int formatLen = format . Length ;
324-
325- if ( formatLen > 1 )
326- {
327- if ( formatLen > 4 )
328- {
329- // Invalid Format
330- #pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
331- throw new ArgumentException ( ) ;
332- #pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one
333- }
334-
335- for ( int i = 1 ; i < formatLen ; i ++ )
336- {
337- var digit = ( ushort ) ( format [ i ] - '0' ) ;
338-
339- if ( digit > 9 )
340- {
341- #pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
342- throw new ArgumentException ( ) ;
343- #pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one
344- }
345-
346- precision = precision * 10 + digit ;
347- }
348- }
349-
350- // Set default precision, if neccessary + check for valid formatCh
351- switch ( formatCh )
352- {
353- case 'G' :
354- break ;
355- case 'X' :
356- case 'F' :
357- case 'N' :
358- case 'D' :
359- if ( formatLen == 1 ) precision = 2 ; // if no precision is specified, use the default
360- break ;
361- default :
362- #pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
363- throw new ArgumentException ( ) ;
364- #pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one
365- }
366- }
298+ private static extern String FormatNative (
299+ Object value ,
300+ bool isInteger ,
301+ String format ,
302+ String numberDecimalSeparator ,
303+ String negativeSign ,
304+ String numberGroupSeparator ,
305+ int [ ] numberGroupSizes ) ;
367306
368- private static String PostProcessInteger ( Object value , String original , char format , int precision , NumberFormatInfo info )
369- {
370- String result = original ;
371-
372- switch ( format )
373- {
374- case 'X' :
375- // truncate negative numbers to
376- if ( result . Length > precision && ( result [ 0 ] == 'F' || result [ 0 ] == 'f' ) )
377- {
378- int len = result . Length ;
379-
380- if ( value is sbyte || value is byte )
381- {
382- if ( len > 2 )
383- {
384- result = result . Substring ( len - 2 , 2 ) ;
385- }
386- }
387- else if ( value is short )
388- {
389- if ( len > 4 )
390- {
391- result = result . Substring ( len - 4 , 4 ) ;
392- }
393- }
394- }
395- break ;
396-
397- case 'N' :
398- // InsertGroupSeparators, AppendTrailingZeros, ReplaceNegativeSign
399- result = InsertGroupSeparators ( result , info ) ;
400- goto case 'F' ; // falls through
401- case 'F' :
402- // AppendTrailingZeros, ReplaceNegativeSign
403- result = AppendTrailingZeros ( result , precision , info ) ;
404- goto case 'G' ; // falls through
405- case 'G' :
406- result = ReplaceNegativeSign ( result , info ) ;
407- break ;
408- }
409-
410- return result ;
411- }
412-
413- private static String PostProcessFloat ( String original , char format , int precision , NumberFormatInfo info )
414- {
415- String result = original ;
416-
417- if ( format == 'N' )
418- {
419- result = InsertGroupSeparators ( result , info ) ;
420- }
421-
422- result = AppendFloatTrailingZeros ( result , precision , info ) ;
423- result = ReplaceDecimalSeperator ( result , info ) ;
424- result = ReplaceNegativeSign ( result , info ) ;
425-
426- return result ;
427- }
428-
429- private static String AppendTrailingZeros ( String original , int count , NumberFormatInfo info )
430- {
431- if ( count > 0 )
432- {
433- return original + info . NumberDecimalSeparator + new String ( '0' , count ) ;
434- }
435- return original ;
436- }
437- private static String AppendFloatTrailingZeros ( String original , int count , NumberFormatInfo info )
438- {
439- // find decimal separator
440- int pos = original . IndexOf ( '.' ) ;
441-
442- if ( pos != - 1 )
443- {
444- // is the string representation missing any trailing zeros?
445- count = ( original . Length - pos ) - count - 1 ;
446- if ( count > 0 )
447- {
448- return original + new String ( '0' , count ) ;
449- }
450- }
451- return original ;
452- }
453-
454- private static String ReplaceNegativeSign ( String original , NumberFormatInfo info )
455- {
456- if ( original [ 0 ] == '-' )
457- {
458- return info . NegativeSign + original . Substring ( 1 ) ;
459- }
460- return original ;
461- }
462-
463- private static String ReplaceDecimalSeperator ( String original , NumberFormatInfo info )
464- {
465- int pos = original . IndexOf ( '.' ) ;
466-
467- if ( pos != - 1 )
468- {
469- return original . Substring ( 0 , pos ) + info . NumberDecimalSeparator + original . Substring ( pos + 1 ) ;
470- }
471- return original ;
472- }
473-
474- private static String InsertGroupSeparators ( String original , NumberFormatInfo info )
475- {
476- int digitsStartPos = ( original [ 0 ] == '-' ) ? 1 : 0 ;
477-
478- int decimalPointPos = original . IndexOf ( '.' ) ;
479- if ( decimalPointPos == - 1 ) decimalPointPos = original . Length ;
480-
481- String prefix = ( digitsStartPos == 1 ) ? "-" : "" ;
482- String suffix = original . Substring ( decimalPointPos ) ;
483- String digits = original . Substring ( digitsStartPos , decimalPointPos - digitsStartPos ) ;
484-
485- String result = String . Empty ;
486-
487- int [ ] groupSizes = info . NumberGroupSizes ;
488-
489- int sizeInd = 0 ;
490- int size = groupSizes [ sizeInd ] ;
491- int pos = digits . Length - size ;
492-
493- String seperator = info . NumberGroupSeparator ;
494- int lastSizeInd = groupSizes . Length - 1 ;
495-
496- while ( pos > 0 )
497- {
498- result = seperator + digits . Substring ( pos , size ) + result ;
499-
500- if ( sizeInd < lastSizeInd )
501- {
502- sizeInd ++ ;
503- size = groupSizes [ sizeInd ] ;
504-
505- if ( size == 0 ) // per spec, when we see a 0, we leave the remaining digits ungrouped.
506- {
507- break ;
508- }
509- }
510-
511- pos -= size ;
512- }
513-
514- result = prefix + digits . Substring ( 0 , size + pos ) + result + suffix ;
515-
516- return result ;
517- }
518307 }
519308}
0 commit comments