ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
numberconversion.cpp
2
3namespace alib { namespace strings {
4
5/// This is a detail namespace of module \alib_strings_nl.
6namespace detail {
7#if !DOXYGEN
8
9
10namespace {
11constexpr const uint64_t pow10_0to19[]=
12{
13 1UL, // 10^ 0
14 10UL, // 10^ 1
15 100UL, // 10^ 2
16 1000UL, // 10^ 3
17 10000UL, // 10^ 4
18 100000UL, // 10^ 5
19 1000000UL, // 10^ 6
20 10000000UL, // 10^ 7
21 100000000UL, // 10^ 8
22 1000000000UL, // 10^ 9
23 10000000000UL, // 10^10
24 100000000000UL, // 10^11
25 1000000000000UL, // 10^12
26 10000000000000UL, // 10^13
27 100000000000000UL, // 10^14
28 1000000000000000UL, // 10^15
29 10000000000000000UL, // 10^16
30 100000000000000000UL, // 10^17
31 1000000000000000000UL, // 10^18
32 10000000000000000000UL, // 10^19
33};
34
35
36constexpr const uint8_t binSizeToDecSize[]
37{
38 20, 19, 19, 19, 19, 18, 18, 18, 17, 17, 17, 16, 16, 16, 16, 15,
39 15, 15, 14, 14, 14, 13, 13, 13, 13, 12, 12, 12, 11, 11, 11, 10,
40 10, 10, 10, 9, 9, 9, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6,
41 05, 5, 5, 4, 4, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1
42};
43
44constexpr inline bool hasBits(NumberFormatFlags lhs, NumberFormatFlags rhs)
45{ return ((int(lhs) & int(rhs)) != 0); }
46
47}// anonymous namespace
48
49
50
51template<typename TChar>
52uint64_t ParseDecDigits( const TString<TChar>& src, integer& idx ) {
53 uint64_t result= 0;
54
55 // check
56 integer length= src.Length();
57 if( idx < 0 || idx >= length )
58 return 0;
59
60 const TChar* buf= src.Buffer();
61
62 // read number
63 while ( idx < length ) {
64 const TChar c= buf[idx];
65 if ( c < '0' || c > '9' )
66 break;
67
68 result= ( result * 10 ) + uint64_t( c - '0' );
69 ++idx;
70 }
71
72 // that's it
73 return result;
74}
75
76// compilers can't know that the parse-methods above leave idx as it is or succeed.
77// Therefore , it would warn that result may not be initialized. But this will never happen.
79
80template<typename TChar>
81int64_t ParseInt( const TString<TChar>& src, integer& startIdx, const TNumberFormat<TChar>& nf ) {
82
83 integer srcLength= src.Length();
84
85 if ( startIdx < 0 || startIdx >= srcLength )
86 return 0;
87
88 // get buffer and index behind whitespaces
89 const TChar* buffer= src.Buffer();
90
91 integer idx= src. template IndexOfAny<lang::Inclusion::Exclude, NC>( nf.Whitespaces, startIdx );
92 if ( idx < 0 )
93 return 0;
94
95 // read sign
96 bool negative;
97 if ( (negative= (buffer[idx] == '-')) == true || buffer[idx] == '+' ) {
98 if( (idx= src. template IndexOfAny<lang::Inclusion::Exclude, NC>( nf.Whitespaces, idx + 1 ) ) == -1 )
99 return 0;
100 }
101
102 int64_t result;
103 integer oldIdx= idx;
104
105 integer prefixLen;
106
107 // try parsing hexadecimal
108 if( ( prefixLen= nf.HexLiteralPrefix.Length()) != 0
109 && ( idx + prefixLen < srcLength )
110 && characters::Equal(buffer + idx, nf.HexLiteralPrefix.Buffer(), prefixLen )
111 && nf.Whitespaces.IndexOf( buffer[ idx + prefixLen ] ) == -1 )
112 {
113 idx+= prefixLen;
114 result= int64_t( ParseHex( src, idx, nf ) );
115 if( idx - prefixLen == oldIdx )
116 idx-= prefixLen;
117 }
118
119 // try parsing binary
120 else if( (prefixLen= nf.BinLiteralPrefix.Length()) != 0
121 && ( idx + prefixLen < srcLength )
122 && characters::Equal(buffer + idx, nf.BinLiteralPrefix.Buffer(), prefixLen )
123 && nf.Whitespaces.IndexOf( buffer[ idx + prefixLen ] ) == -1 )
124 {
125 idx+= prefixLen;
126 result= int64_t( ParseBin( src, idx, nf ) );
127 if( idx - prefixLen == oldIdx )
128 idx-= prefixLen;
129 }
130
131 // try parsing octal
132 else if( (prefixLen= nf.OctLiteralPrefix.Length()) != 0
133 && ( idx + prefixLen < srcLength )
134 && characters::Equal(buffer + idx, nf.OctLiteralPrefix.Buffer(), prefixLen )
135 && nf.Whitespaces.IndexOf( buffer[ idx + prefixLen ] ) == -1 )
136 {
137 idx+= prefixLen;
138 result= int64_t( ParseOct( src, idx, nf ) );
139 if( idx - prefixLen == oldIdx )
140 idx-= prefixLen;
141 }
142
143
144 // parse decimal
145 if ( idx == oldIdx )
146 result= int64_t( ParseDec( src, idx, nf ) );
147
148 // Any success? if yes, we move the given start pointer
149
150 if ( idx != oldIdx )
151 startIdx= idx;
152
153
154 return negative ? int64_t(-result)
155 : int64_t( result);
156}
157
159
160template<typename TChar>
161uint64_t ParseDec( const TString<TChar>& src, integer& startIdx, const TNumberFormat<TChar>& nf ) {
162 uint64_t result= 0;
163
164 // read whitespaces
165 integer idx= src. template IndexOfAny<lang::Inclusion::Exclude>( nf.Whitespaces, startIdx );
166 if ( idx < 0 )
167 return 0;
168
169 // read number
170 bool charFound= false;
171 integer length= src.Length();
172 const TChar* buf= src.Buffer();
173 while ( idx < length ) {
174 const TChar c= buf[idx];
175 if ( charFound
176 && hasBits(nf.Flags, NumberFormatFlags::ReadGroupChars)
177 && c != '\0'
178 && c == nf.ThousandsGroupChar )
179 {
180 ++idx;
181 continue;
182 }
183
184 if ( c < '0' || c > '9' )
185 break;
186
187 charFound= true;
188 result= ( result * 10 ) + uint64_t( c - '0' );
189
190 ++idx;
191 }
192
193 if ( charFound )
194 startIdx= idx;
195 return result;
196}
197
198
199template<typename TChar>
200uint64_t ParseBin( const TString<TChar>& src, integer& startIdx, const TNumberFormat<TChar>& nf ) {
201 uint64_t result= 0;
202
203 // read whitespaces
204 integer idx= src. template IndexOfAny<lang::Inclusion::Exclude>( nf.Whitespaces, startIdx );
205 if ( idx < 0 )
206 return 0;
207
208 // read number
209 bool charFound= false;
210 integer length = src.Length();
211 const TChar* buf = src.Buffer();
212 while ( idx < length ) {
213 TChar c= buf[idx];
214 if ( charFound
215 && hasBits(nf.Flags, NumberFormatFlags::ReadGroupChars)
216 && c != '\0'
217 && ( c == nf.BinNibbleGroupChar
218 || c == nf.BinByteGroupChar
219 || c == nf.BinWordGroupChar
220 || c == nf.BinWord32GroupChar ) )
221 {
222 ++idx;
223 continue;
224 }
225
226 if ( c == '0' ) {
227 result<<= 1;
228 ++idx;
229 charFound= true;
230 continue;
231 }
232
233 if ( c == '1' ) {
234 result<<= 1;
235 result|=1;
236 ++idx;
237 charFound= true;
238 continue;
239 }
240
241 break;
242 }
243
244 if ( charFound )
245 startIdx= idx;
246 return result;
247}
248
249template<typename TChar>
250uint64_t ParseHex( const TString<TChar>& src, integer& startIdx, const TNumberFormat<TChar>& nf ) {
251 uint64_t result= 0;
252
253 // read whitespaces
254 integer idx= src. template IndexOfAny<lang::Inclusion::Exclude>( nf.Whitespaces, startIdx );
255 if ( idx < 0 )
256 return 0;
257
258 // read number
259 bool charFound= false;
260 integer length= src.Length();
261 const TChar* buf= src.Buffer();
262 while ( idx < length ) {
263 TChar c= buf[idx];
264 if ( charFound
265 && hasBits(nf.Flags, NumberFormatFlags::ReadGroupChars)
266 && c != '\0'
267 && ( c == nf.HexByteGroupChar
268 || c == nf.HexWordGroupChar
269 || c == nf.HexWord32GroupChar ) )
270 {
271 ++idx;
272 continue;
273 }
274
275 if ( c >= '0' && c <= '9' ) {
276 result<<= 4;
277 result|= uint64_t(c - '0');
278 ++idx;
279 charFound= true;
280 continue;
281 }
282
283 if ( c >= 'A' && c <= 'F' ) {
284 result<<= 4;
285 result|= uint64_t(c - 'A' + 10 );
286 ++idx;
287 charFound= true;
288 continue;
289 }
290
291 if ( c >= 'a' && c <= 'f' ) {
292 result<<= 4;
293 result|= uint64_t(c - 'a' + 10 );
294 ++idx;
295 charFound= true;
296 continue;
297 }
298
299
300 break;
301 }
302
303 if ( charFound )
304 startIdx= idx;
305 return result;
306}
307
308template<typename TChar>
309uint64_t ParseOct( const TString<TChar>& src, integer& startIdx, const TNumberFormat<TChar>& nf ) {
310 uint64_t result= 0;
311
312 // read whitespaces
313 integer idx= src. template IndexOfAny<lang::Inclusion::Exclude>( nf.Whitespaces, startIdx );
314 if ( idx < 0 )
315 return 0;
316
317 // read number
318 bool charFound= false;
319 integer length= src.Length();
320 const TChar* buf= src.Buffer();
321 while ( idx < length ) {
322 TChar c= buf[idx];
323 if ( charFound
324 && hasBits(nf.Flags, NumberFormatFlags::ReadGroupChars)
325 && c != 0
326 && c == nf.OctGroupChar )
327 {
328 ++idx;
329 continue;
330 }
331
332 if ( c >= '0' && c <= '7' ) {
333 result<<= 3;
334 result|= uint64_t(c - '0');
335 ++idx;
336 charFound= true;
337 continue;
338 }
339
340 break;
341 }
342
343 if ( charFound )
344 startIdx= idx;
345 return result;
346}
347
348template<typename TChar>
349double ParseFloat( const TString<TChar>& src, integer& startIdx, const TNumberFormat<TChar>& nf ) {
350 // checks
351 if( startIdx < 0 || startIdx >= src.Length() )
352 return 0.0;
353
354 // get pointers
355 const TChar* buf= src.Buffer() + startIdx;
356 const TChar* bufEnd= src.Buffer() + src.Length();
357
358 // read whitespaces
359 {
360 integer skip= src. template IndexOfAny<lang::Inclusion::Exclude>( nf.Whitespaces, startIdx );
361 if ( skip < 0 )
362 return 0.0;
363 buf+= skip - startIdx;
364 }
365
366 // +/- sign
367 bool negative= (*buf == '-');
368 if ( negative || *buf == '+' ) {
369 if( ++buf == bufEnd )
370 return 0.0;
371
372 integer skip= TString<TChar>(buf, bufEnd-buf ). template IndexOfAny<lang::Inclusion::Exclude, NC>( nf.Whitespaces );
373 if( skip < 0 )
374 return 0.0;
375 buf+= skip;
376 }
377
378 // NaN, Infinite
379 if( buf + nf.NANLiteral.Length() - 1 <= bufEnd
380 && nf.NANLiteral. template CompareTo<CHK, lang::Case::Ignore>( TString<TChar>( buf, nf.NANLiteral.Length() ) ) == 0 )
381 {
382 startIdx= buf - src.Buffer() + nf.NANLiteral.Length();
383 return std::numeric_limits<double>::quiet_NaN();
384 }
385
386 if( buf + nf.INFLiteral.Length() - 1 <= bufEnd
387 && nf.INFLiteral. template CompareTo<CHK, lang::Case::Ignore>( TString<TChar>( buf, nf.INFLiteral.Length() ) ) == 0 )
388 {
389 startIdx= buf - src.Buffer() + nf.INFLiteral.Length();
390 return negative ? -std::numeric_limits<double>::infinity()
391 : std::numeric_limits<double>::infinity();
392 }
393
394 double result= 0.0;
395
396 // read number before dot?
397 TChar c= *buf;
398 bool integralPartFound= isdigit(int(c));
399 if ( integralPartFound ) {
400 if ( c < '0' || c > '9' )
401 return 0.0;
402
403 {
404 integer intIdx= 0;
405 result= double( ParseDec( TString<TChar>(buf, bufEnd - buf), intIdx, nf ) );
406 buf+= intIdx;
407 }
408
409 // end?
410 ALIB_ASSERT_ERROR( buf <= bufEnd, "STRINGS", "Error in float parsing algorithm." )
411 if ( buf == bufEnd ) {
412 startIdx= buf - src.Buffer();
413 return negative ? -result : result;
414 } }
415
416 if( nf.DecimalPointChar == *buf ) {
417 // consume dot
418 ++buf;
419
420 // read number after dot
421 if ( buf < bufEnd
422 && *buf >= '0' && *buf <= '9' )
423 {
424 integer intIdx = 0;
425 double intValue= double( ParseDecDigits( TString<TChar>(buf, bufEnd - buf), intIdx) );
426 buf+= intIdx;
427 result+= ( intValue / pow( 10, double(intIdx) ) );
428 } }
429 else if( !integralPartFound )
430 return 0.0; // index not moved, indicates failure.
431
432 // read eNNN
433 if ( buf < bufEnd ) {
434 auto oldBuf= buf;
435 bool eSepFound= false;
436 integer sepLen= nf.ExponentSeparator.Length();
437 if ( buf + sepLen < bufEnd ) {
438 integer pos= 0;
439 while ( pos < sepLen
440 && nf.ExponentSeparator. template CharAt<NC>(pos) == *(buf + pos) )
441 ++pos;
442 if ( (eSepFound= ( pos == sepLen ) ) == true )
443 buf += pos;
444 }
445 if ( !eSepFound && ( *buf == 'e' || *buf == 'E' ) ) {
446 ++buf;
447 eSepFound= true;
448 }
449
450
451 if (eSepFound && buf < bufEnd) {
452 bool negativeE= false;
453 if ( (negativeE= (*buf == '-') ) == true || *buf == '+' )
454 ++buf;
455
456 if( buf < bufEnd ) {
457 integer idx= 0;
458 int exp= int( ParseDecDigits( TString<TChar>(buf, bufEnd - buf), idx ) );
459 if( idx > 0 ) {
460 buf+= idx;
461 result*= pow( 10, negativeE ? -exp : exp );
462 } else {
463 // no number found behind e. restore buf and ignore.
464 buf= oldBuf;
465 } } } }
466
467 // adjust given idx
468 startIdx= buf - src.Buffer();
469
470 // that's it
471 return negative ? -result : result;
472}
473
474template<typename TChar>
475integer WriteDecUnsigned( uint64_t value, TChar* buffer, integer idx, int overrideWidth,
476 const TNumberFormat<TChar>& nf ) {
477 int width= overrideWidth != 0 ? overrideWidth
479
480 if ( width < 1 ) width= 1;
481
482 // get the number of dec digits in value
483 int digitsInValue;
484 {
485 if( value < 10 )
486 digitsInValue = 1;
487 else {
488 int leadingBinaryZeros= lang::CLZ(value);
489 digitsInValue= binSizeToDecSize[leadingBinaryZeros];
490
491 // could be one lower due to the rest after the most significant bit
492 if( value < pow10_0to19[digitsInValue-1] )
493 --digitsInValue;
494
495 ALIB_ASSERT_ERROR( value >= pow10_0to19[digitsInValue-1]
496 && (digitsInValue == 20 || value < pow10_0to19[digitsInValue ]),
497 "STRINGS", "Error calculating the number of digits in value {}",
498 value )
499 } }
500
501
502 // calc printable digits and print leading group char replacement ' '
503 int printDigits;
504 {
505 int requestedDigits;
506 if ( hasBits(nf.Flags, NumberFormatFlags::WriteGroupChars) && nf.ThousandsGroupChar != '\0' ) {
507 if ( width > 26 )
508 width= 26;
509 requestedDigits= width - width / 4;
510 } else {
511 if ( width > 20 )
512 width= 20;
513 requestedDigits= width;
514 }
515
516 printDigits= (std::max)( requestedDigits, digitsInValue );
517
518
519 // starts with group character? -> write space instead
520 ALIB_ASSERT_ERROR( width - 1 <= printDigits + (printDigits - 1) / 3 ,
521 "STRINGS", "Internal error, false assumption" )
522 if( printDigits >1 && width > printDigits + (printDigits - 1) / 3 )
523 buffer[idx++]= nf.LeadingGroupCharReplacement;
524 }
525
526 // main loop
527 bool printSpace= hasBits(nf.Flags, NumberFormatFlags::ReplaceLeadingZerosWithSpaces);
528 int actDigit= printDigits;
529 while ( actDigit > 0 ) {
530 // print normal digit
531 int digitValue= int( ( value / pow10_0to19[actDigit-1] ) );
532 ALIB_ASSERT( digitValue <= 9, "STRINGS" )
533
534 // write group character
535 if( hasBits(nf.Flags, NumberFormatFlags::WriteGroupChars) && nf.ThousandsGroupChar != '\0'
536 && actDigit != printDigits
537 && actDigit % 3 == 0
538 )
539 buffer[idx++]= printSpace ? TChar(' ') : nf.ThousandsGroupChar;
540
541 // write character
542 printSpace&= (digitValue == 0 && actDigit > 1);
543 buffer[idx++]= printSpace ? TChar(' ') : TChar( 48 + digitValue ); // 48= '0'
544
545 // next
546 value= value % pow10_0to19[actDigit-1];
547
548 --actDigit;
549 }
550
551 return idx;
552}
553
554template<typename TChar>
555integer WriteDecSigned( int64_t value, TChar* buffer, integer idx, int overrideWidth,
556 const TNumberFormat<TChar>& nf ) {
557 integer oldIdx= idx;
558
559 // write sign and turn negative to positive
560 uint64_t uValue;
561 if ( value >= 0 ) {
562 uValue= uint64_t( value );
563 if ( nf.PlusSign != '\0' )
564 buffer[idx++]= nf.PlusSign;
565 } else {
566 uValue= uint64_t( -value );
567 buffer[idx++]= '-';
568 }
569
570 int width= overrideWidth != 0 ? overrideWidth
572 if( idx != oldIdx && width > 1 )
573 --width;
574
575 return WriteDecUnsigned( uValue, buffer, idx, width, nf );
576}
577
578
579template<typename TChar>
580integer WriteBin( uint64_t value, TChar* buffer, integer idx, int overrideWidth,
581 const TNumberFormat<TChar>& nf ) {
582 // how many digits in a grouping block?
583 int groupWidth= !hasBits(nf.Flags, NumberFormatFlags::WriteGroupChars) ? 0
584 : nf.BinNibbleGroupChar != '\0' ? 4
585 : nf.BinByteGroupChar != '\0' ? 8
586 : nf.BinWordGroupChar != '\0' ? 16
587 : nf.BinWord32GroupChar != '\0' ? 32 : 0;
588
589 // if the first "digit" is a separator, we will write a space instead.
590 // (we do the best to keep the width of the output intact if given)
591 int nextSeparator= 0; // 0: dont write, 1= space, 2 normal
592
593 // adjust minDigits to 0..64 (if 0 demanded, 0 is given!)
594 int digits= overrideWidth != 0 ? overrideWidth : nf.BinFieldWidth;
595 if (digits > 0 ) {
596 if ( groupWidth != 0 ) {
597 nextSeparator= digits<= groupWidth ? 0
598 : ( digits >= groupWidth
599 && (digits % (groupWidth + 1)) == 0 ) ? 1 : 2;
600
601 digits-= digits / (groupWidth + 1); // subtract needed separators from digits
602 }
603
604 // still oversized?
605 if ( digits > 64 )
606 digits= 64;
607 }
608
609 // if negative value given, we calculate the needed digits
610 if ( digits < 0 )
611 digits= value != 0 ? lang::MSB(value)
612 : 1;
613
614 uint64_t testValue= uint64_t(1) << (digits - 1);
615 while ( digits > 0) {
616 // write the separator
617 if( groupWidth != 0 && ( digits % groupWidth) == 0 ) {
618 if ( nextSeparator != 0 )
619 buffer[idx++]= nextSeparator == 1 ? nf.LeadingGroupCharReplacement :
620 ( digits % 32 == 0 ) ? nf.BinWord32GroupChar :
621 ( digits % 16 == 0 ) ? nf.BinWordGroupChar :
622 ( digits % 8 == 0 ) ? nf.BinByteGroupChar : nf.BinNibbleGroupChar;
623 }
624 nextSeparator= 2; // from now on write separators
625
626 // write digit
627 buffer[idx++]= ( value & testValue ) == 0L ? '0' : '1';
628
629 // next
630 testValue >>= 1;
631 --digits;
632 }
633
634 return idx;
635}
636
637template<typename TChar>
638integer WriteHex( uint64_t value, TChar* buffer, integer idx, int overrideWidth,
639 const TNumberFormat<TChar>& nf ) {
640 // how many digits in a grouping block?
641 int groupWidth= !hasBits(nf.Flags, NumberFormatFlags::WriteGroupChars) ? 0
642 : nf.HexByteGroupChar != '\0' ? 2
643 : nf.HexWordGroupChar != '\0' ? 4
644 : nf.HexWord32GroupChar != '\0' ? 8 : 0;
645
646 // if the first "digit" is a separator, we will write a space instead.
647 // (we do the best to keep the width of the output intact if given)
648 int nextSeparator= 0; // 0: dont write, 1= space, 2 normal
649
650 int digits= overrideWidth != 0 ? overrideWidth : nf.HexFieldWidth;
651
652 // adjust minDigits to 0..64 (if 0 demanded, 0 is given!)
653 if (digits > 0 ) {
654 if ( groupWidth != 0 ) {
655 nextSeparator= digits<= groupWidth ? 0
656 : ( digits >= groupWidth
657 && (digits % (groupWidth + 1)) == 0 ) ? 1 : 2;
658
659 digits-= digits / (groupWidth + 1); // subtract needed separators from digits
660 } }
661
662 // if negative value given, we calculate the needed digits
663 if ( digits < 0 )
664 digits= value != 0 ? (lang::MSB(value)-1) / 4 + 1
665 : 1;
666
667 // check now for oversize
668 if ( digits > 16 )
669 digits= 16;
670
671 unsigned characterA= static_cast<unsigned int>( hasBits(nf.Flags, NumberFormatFlags::HexLowerCase) ? 'a' : 'A' );
672 int shiftBits= (digits -1 ) * 4;
673 uint64_t testMask= uint64_t( 15 ) << shiftBits;
674 while ( digits > 0) {
675 // write the separator
676 if( groupWidth != 0 && ( digits % groupWidth) == 0 ) {
677 if ( nextSeparator != 0 )
678 buffer[idx++]= nextSeparator == 1 ? nf.LeadingGroupCharReplacement :
679 ( digits % 8 == 0 ) ? nf.HexWord32GroupChar :
680 ( digits % 4 == 0 ) ? nf.HexWordGroupChar : nf.HexByteGroupChar;
681 }
682 nextSeparator= 2; // from now on write separators
683
684 // write digit
685 unsigned nibble= static_cast<unsigned int>( ( value & testMask ) >> shiftBits );
686 buffer[idx++]= ( nibble < 10 ) ? static_cast<TChar>('0' + nibble)
687 : static_cast<TChar>(characterA + (nibble - 10));
688
689 // next
690 testMask >>= 4;
691 shiftBits -= 4;
692 --digits;
693 }
694
695 return idx;
696}
697
698template<typename TChar>
699integer WriteOct( uint64_t value, TChar* buffer, integer idx, int overrideWidth,
700 const TNumberFormat<TChar>& nf ) {
701 // how many digits in a grouping block?
702 const int groupWidth= hasBits(nf.Flags, NumberFormatFlags::WriteGroupChars) && nf.OctGroupChar != '\0' ? 3 : 0;
703
704 // if the first "digit" is a separator, we will write a space instead.
705 // (we do the best to keep the width of the output intact if given)
706 int nextSeparator= 0; // 0: dont write, 1= space, 2 normal
707
708 // adjust minDigits to 0..64 (if 0 demanded, 0 is given!)
709 int digits= overrideWidth != 0 ? overrideWidth : nf.OctFieldWidth;
710 if (digits > 0 ) {
711 if ( groupWidth != 0 ) {
712 nextSeparator= digits<= groupWidth ? 0
713 : ( digits >= groupWidth
714 && (digits % (groupWidth + 1)) == 0 ) ? 1 : 2;
715
716 digits-= digits / (groupWidth + 1); // subtract needed separators from digits
717 } }
718
719 // if negative value given, we calculate the needed digits
720 if ( digits < 0 )
721 digits= value != 0 ? (lang::MSB(value)-1) / 3 + 1
722 : 1;
723
724 // check now for oversize
725 if ( digits > 22 )
726 digits= 22;
727
728 int shiftBits= (digits -1 ) * 3;
729 while ( digits > 0) {
730 // write the separator
731 if( groupWidth != 0 && ( digits % groupWidth) == 0 ) {
732 if ( nextSeparator != 0 )
733 buffer[idx++]= nextSeparator == 1 ? nf.LeadingGroupCharReplacement : nf.OctGroupChar;
734 }
735 nextSeparator= 2; // from now on write separators
736
737 // write digit
738 auto octet= ( value & (uint64_t(7) << shiftBits) ) >> shiftBits;
739 buffer[idx++]= static_cast<TChar>('0' + octet);
740
741 // next
742 shiftBits -= 3;
743 --digits;
744 }
745
746 return idx;
747}
748
749template<typename TChar>
750integer WriteFloat( double value, TChar* buffer, integer idx, int overrideWidth,
751 const TNumberFormat<TChar>& nf ) {
752 int integralWidth= overrideWidth != 0 ? overrideWidth : nf.IntegralPartMinimumWidth;
753
754
755 // check for NaN, negative zero
756 auto classification= std::fpclassify(value);
757 if( classification == FP_NAN ) { return idx+= nf.NANLiteral.CopyTo(buffer + idx); }
758 bool isNegative= std::signbit(value);
759 if ( isNegative ) {
760 if( classification == FP_ZERO ) {
761 isNegative= false;
762 value = 0.0;
763 }
764 else
765 value= -value;
766 }
767
768 // +/-inf
769 if( classification == FP_INFINITE ) {
770 if ( isNegative )
771 buffer[idx++]= '-';
772 else if ( nf.PlusSign != '\0' )
773 buffer[idx++]= nf.PlusSign;
774 return idx+= nf.INFLiteral.CopyTo(buffer + idx);
775 }
776
777
778 constexpr int MaxFloatSignificantDigits= 16;
779
780
781 // calc dot position
782 int exp10= value != 0.0 ? int( floor(log10( value ) ) )
783 : 0;
784
785 // decide if we are using scientific format (with e) or not
786 bool scientific= ( hasBits(nf.Flags, NumberFormatFlags::ForceScientific)
787 || ( integralWidth < 0 && nf.FractionalPartWidth < 0 && ( exp10 > 6 || exp10 <= -5 ) )
788 || ( integralWidth > 0 && exp10 != 0 && exp10 >= (MaxFloatSignificantDigits - integralWidth - 1 ) )
789 || ( nf.FractionalPartWidth > 0 && exp10 != 0 && exp10 >= (MaxFloatSignificantDigits - nf.FractionalPartWidth - 1 ) )
790 );
791
792 integralWidth= (std::min) ( integralWidth, 15 );
793 int fractionalDigits= (std::min) ( nf.FractionalPartWidth, int8_t(15) );
794
795
796
797 // result variables used for output
798 uint64_t intPart;
799 uint64_t fractPart;
800 int unusedFractDigits;
801 int firstNonZero;
802
803 // scientific output
804 if ( scientific ) {
805 auto dotPos= MaxFloatSignificantDigits - exp10;
806 intPart= uint64_t(llrint( value * pow( 10, dotPos ) ));
807 fractPart= intPart % pow10_0to19[ MaxFloatSignificantDigits ];
808 intPart= intPart / pow10_0to19[ MaxFloatSignificantDigits ];
809
810 // determine first non zero fract number
811 firstNonZero= 0;
812 if ( fractPart > 0 ) {
813 ALIB_ASSERT( MaxFloatSignificantDigits - firstNonZero < 20, "STRINGS")
814 while ( fractPart < pow10_0to19[ MaxFloatSignificantDigits - firstNonZero - 1 ] )
815 ++firstNonZero;
816 ALIB_ASSERT( MaxFloatSignificantDigits - firstNonZero > 0, "STRINGS")
817 }
818 ++firstNonZero;
819
820 unusedFractDigits= fractionalDigits >= 0 ? MaxFloatSignificantDigits - fractionalDigits
821 : 1;
822 }
823
824 // normal output, number > 0
825 else if (exp10 >= 0 ) {
826 int intPartSize= MaxFloatSignificantDigits - exp10;
827 ALIB_ASSERT( intPartSize > 0 && intPartSize <= MaxFloatSignificantDigits, "STRINGS" )
828 intPart= uint64_t(llrint( value * pow( 10, intPartSize ) ));
829 fractPart= intPart % pow10_0to19[ intPartSize ];
830 intPart= intPart / pow10_0to19[ intPartSize ];
831
832 // determine first non zero fract number
833 firstNonZero= 0;
834 if ( fractPart > 0 ) {
835 while ( fractPart < pow10_0to19[ intPartSize - firstNonZero - 1 ] )
836 ++firstNonZero;
837 ALIB_ASSERT( intPartSize - firstNonZero > 0, "STRINGS" )
838 }
839 ++firstNonZero;
840
841 unusedFractDigits= fractionalDigits >= 0 ? intPartSize - fractionalDigits
842 : 1;
843 }
844
845 // normal output, number < 1
846 else {
847 // just zeros? -> write them and return
848 firstNonZero= -exp10;
849 intPart= 0;
850 fractPart= uint64_t(llrint( value * pow( 10, MaxFloatSignificantDigits + firstNonZero) ));
851 unusedFractDigits= fractionalDigits >= 0 ? MaxFloatSignificantDigits - ( fractionalDigits - firstNonZero )
852 : 1;
853 }
854
855 // cut fract digits and round it up
856 if ( (fractionalDigits < 0 || fractionalDigits >= firstNonZero - 1 )
857 && unusedFractDigits > 0
858 && unusedFractDigits <= 18 )
859 {
860
861 uint64_t rest= fractPart % pow10_0to19[ unusedFractDigits ];
862 fractPart = fractPart / pow10_0to19[ unusedFractDigits ];
863 if ( rest > pow10_0to19[ unusedFractDigits ] / 2 ) {
864 ++fractPart;
865 int overflowDigit= 0;
866 bool overflow= false;
867 while ( (overflowDigit <= fractionalDigits || fractionalDigits < 0 )
868 && (overflow|= fractPart == pow10_0to19[ overflowDigit ]) == false
869 && fractPart > pow10_0to19[ overflowDigit ]
870 )
871 ++overflowDigit;
872
873 if ( overflow ) {
874 if ( overflowDigit == (fractionalDigits >= 0 ? fractionalDigits : 15) ) {
875 fractPart= 0;
876 ++intPart;
877 } else {
878 ALIB_ASSERT( firstNonZero > 1, "STRINGS" )
879 --firstNonZero;
880 } } } }
881
882 // write sign. Do it only if this is not a 0 value after rounding.
883 if ( isNegative ) {
884 if( intPart
885 || ( fractPart
886 && ( fractionalDigits < 0 || fractionalDigits > firstNonZero -1)
887 ) )
888 buffer[idx++]= '-';
889 }
890 else if ( nf.PlusSign != '\0' )
891 buffer[idx++]= nf.PlusSign;
892
893 // write int part
894 if ( intPart != 0L || integralWidth != 0 )
895 idx= WriteDecUnsigned( intPart, buffer, idx, integralWidth, nf );
896
897 // write dot
898 if ( fractionalDigits != 0 || hasBits(nf.Flags, NumberFormatFlags::ForceDecimalPoint) )
899 buffer[idx++]= nf.DecimalPointChar;
900
901 // write fract part
902 if (fractionalDigits != 0) {
903 int fractZeros= firstNonZero - 1;
904 if ( fractionalDigits > 0 && fractZeros > fractionalDigits )
905 fractZeros= fractionalDigits;
906
907 for ( int i= 0 ; i < fractZeros ; ++i )
908 buffer[idx++]= '0';
909
910 int qtyDigits= fractionalDigits - fractZeros;
911 int actDigit= MaxFloatSignificantDigits + 1;
912 int cntOmittedZeros= 0;
913 int cntDigits= 0;
914 bool printStarted= false;
915 while ( fractPart > 0
916 && ( qtyDigits< 0 || cntDigits < qtyDigits )
917 )
918 {
919 --actDigit;
920
921 // get next d
922 int digitValue= int( ( fractPart / pow10_0to19[actDigit] ) );
923
924 ALIB_ASSERT( digitValue <= 9, "STRINGS" )
925
926 // don't write, yet?
927 if ( (printStarted|= (digitValue != 0)) == false )
928 continue;
929 ++cntDigits;
930
931 // write digit unless its a '0'
932 if ( digitValue == 0 )
933 ++cntOmittedZeros;
934 else {
935 for ( int i= 0; i< cntOmittedZeros ; ++i )
936 buffer[idx++]= '0';
937 cntOmittedZeros= 0;
938 buffer[idx++]= static_cast<TChar>( 48 + digitValue ); // 48= '0'
939 }
940
941 // next
942 fractPart= fractPart % pow10_0to19[actDigit];
943 }
944
945 // ensure that if -1 for fractDigits if given,at least 1 digit is printed
946 if (fractionalDigits < 0 )
947 qtyDigits= 1;
948
949 // write omitted zeros
950 if ( cntDigits < qtyDigits ) {
951 if ( hasBits(nf.Flags, NumberFormatFlags::OmitTrailingFractionalZeros) ) {
952 if( cntDigits == 0 )
953 buffer[idx++]= '0';
954 } else {
955 for ( int i= 0; i< cntOmittedZeros ; ++i )
956 buffer[idx++]= '0';
957 cntDigits+= cntOmittedZeros;
958
959 // write missing digits
960 for ( int i= cntDigits; i< qtyDigits; ++i )
961 buffer[idx++]= '0';
962 } } }
963
964 // write eNN
965 if ( scientific ) {
966 int p= 0;
967 while( nf.ExponentSeparator[p] != '\0' )
968 buffer[idx++]= nf.ExponentSeparator[p++];
969
970 if ( exp10 < 0 )
971 buffer[idx++]= '-';
972 else if ( hasBits(nf.Flags, NumberFormatFlags::WriteExponentPlusSign) )
973 buffer[idx++]= '+';
974
975 idx= WriteDecUnsigned( uint64_t(exp10 >= 0 ? exp10 : -exp10), buffer, idx, 2, nf );
976 }
977
978 return idx;
979}
980
981
982template uint64_t ParseDecDigits <nchar>( const TString<nchar>&, integer& );
983template int64_t ParseInt <nchar>( const TString<nchar>&, integer&, const TNumberFormat<nchar>& );
984template uint64_t ParseDec <nchar>( const TString<nchar>&, integer&, const TNumberFormat<nchar>& );
985template uint64_t ParseBin <nchar>( const TString<nchar>&, integer&, const TNumberFormat<nchar>& );
986template uint64_t ParseHex <nchar>( const TString<nchar>&, integer&, const TNumberFormat<nchar>& );
987template uint64_t ParseOct <nchar>( const TString<nchar>&, integer&, const TNumberFormat<nchar>& );
988template double ParseFloat <nchar>( const TString<nchar>&, integer&, const TNumberFormat<nchar>& );
989template integer WriteDecUnsigned<nchar>( uint64_t, nchar*, integer, int , const TNumberFormat<nchar>& );
990template integer WriteDecSigned <nchar>( int64_t , nchar*, integer, int , const TNumberFormat<nchar>& );
991template integer WriteBin <nchar>( uint64_t, nchar*, integer, int , const TNumberFormat<nchar>& );
992template integer WriteHex <nchar>( uint64_t, nchar*, integer, int , const TNumberFormat<nchar>& );
993template integer WriteOct <nchar>( uint64_t, nchar*, integer, int , const TNumberFormat<nchar>& );
994template integer WriteFloat <nchar>( double , nchar*, integer, int , const TNumberFormat<nchar>& );
995
996template uint64_t ParseDecDigits <wchar>( const TString<wchar>&, integer& );
997template int64_t ParseInt <wchar>( const TString<wchar>&, integer&, const TNumberFormat<wchar>& );
998template uint64_t ParseDec <wchar>( const TString<wchar>&, integer&, const TNumberFormat<wchar>& );
999template uint64_t ParseBin <wchar>( const TString<wchar>&, integer&, const TNumberFormat<wchar>& );
1000template uint64_t ParseHex <wchar>( const TString<wchar>&, integer&, const TNumberFormat<wchar>& );
1001template uint64_t ParseOct <wchar>( const TString<wchar>&, integer&, const TNumberFormat<wchar>& );
1002template double ParseFloat <wchar>( const TString<wchar>&, integer&, const TNumberFormat<wchar>& );
1003template integer WriteDecUnsigned<wchar>( uint64_t, wchar*, integer, int , const TNumberFormat<wchar>& );
1004template integer WriteDecSigned <wchar>( int64_t , wchar*, integer, int , const TNumberFormat<wchar>& );
1005template integer WriteBin <wchar>( uint64_t, wchar*, integer, int , const TNumberFormat<wchar>& );
1006template integer WriteHex <wchar>( uint64_t, wchar*, integer, int , const TNumberFormat<wchar>& );
1007template integer WriteOct <wchar>( uint64_t, wchar*, integer, int , const TNumberFormat<wchar>& );
1008template integer WriteFloat <wchar>( double , wchar*, integer, int , const TNumberFormat<wchar>& );
1009
1010template uint64_t ParseDecDigits <xchar>( const TString<xchar>&, integer& );
1011template int64_t ParseInt <xchar>( const TString<xchar>&, integer&, const TNumberFormat<xchar>& );
1012template uint64_t ParseDec <xchar>( const TString<xchar>&, integer&, const TNumberFormat<xchar>& );
1013template uint64_t ParseBin <xchar>( const TString<xchar>&, integer&, const TNumberFormat<xchar>& );
1014template uint64_t ParseHex <xchar>( const TString<xchar>&, integer&, const TNumberFormat<xchar>& );
1015template uint64_t ParseOct <xchar>( const TString<xchar>&, integer&, const TNumberFormat<xchar>& );
1016template double ParseFloat <xchar>( const TString<xchar>&, integer&, const TNumberFormat<xchar>& );
1017template integer WriteDecUnsigned<xchar>( uint64_t, xchar*, integer, int , const TNumberFormat<xchar>& );
1018template integer WriteDecSigned <xchar>( int64_t , xchar*, integer, int , const TNumberFormat<xchar>& );
1019template integer WriteBin <xchar>( uint64_t, xchar*, integer, int , const TNumberFormat<xchar>& );
1020template integer WriteHex <xchar>( uint64_t, xchar*, integer, int , const TNumberFormat<xchar>& );
1021template integer WriteOct <xchar>( uint64_t, xchar*, integer, int , const TNumberFormat<xchar>& );
1022template integer WriteFloat <xchar>( double , xchar*, integer, int , const TNumberFormat<xchar>& );
1023
1024# include "ALib.Lang.CIMethods.H"
1025
1026
1027#endif // !DOXYGEN
1028}}} // namespace [alib::strings::detail]
#define ALIB_ASSERT(cond, domain)
#define ALIB_ALLOW_UNINITIALIZED
#define ALIB_POP_ALLOWANCE
#define ALIB_ASSERT_ERROR(cond, domain,...)
constexpr integer Length() const
Definition string.hpp:300
constexpr const TChar * Buffer() const
Definition string.hpp:295
bool Equal(TChar lhs, TRhs rhs)
Definition functions.hpp:62
constexpr int MSB(TIntegral value)
Definition bits.hpp:309
constexpr int CLZ(TIntegral value)
Definition bits.hpp:227
This is a detail namespace of module ALib Strings.
uint64_t ParseOct(const TString< TChar > &src, integer &idx, const TNumberFormat< TChar > &nf)
uint64_t ParseHex(const TString< TChar > &src, integer &idx, const TNumberFormat< TChar > &nf)
integer WriteHex(uint64_t value, TChar *buffer, integer idx, int minWidth, const TNumberFormat< TChar > &nf)
int64_t ParseInt(const TString< TChar > &src, integer &idx, const TNumberFormat< TChar > &nf)
integer WriteDecUnsigned(uint64_t value, TChar *buffer, integer idx, int minWidth, const TNumberFormat< TChar > &nf)
uint64_t ParseDecDigits(const TString< TChar > &src, integer &idx)
integer WriteDecSigned(int64_t value, TChar *buffer, integer idx, int minWidth, const TNumberFormat< TChar > &nf)
double ParseFloat(const TString< TChar > &src, integer &idx, const TNumberFormat< TChar > &nf)
uint64_t ParseDec(const TString< TChar > &src, integer &idx, const TNumberFormat< TChar > &nf)
integer WriteOct(uint64_t value, TChar *buffer, integer idx, int minWidth, const TNumberFormat< TChar > &nf)
integer WriteBin(uint64_t value, TChar *buffer, integer idx, int minWidth, const TNumberFormat< TChar > &nf)
integer WriteFloat(double value, TChar *buffer, integer idx, int minWidth, const TNumberFormat< TChar > &nf)
uint64_t ParseBin(const TString< TChar > &src, integer &idx, const TNumberFormat< TChar > &nf)
Definition alox.cpp:14
characters::wchar wchar
Type alias in namespace #"%alib".
lang::integer integer
Type alias in namespace #"%alib".
Definition integers.hpp:149
characters::nchar nchar
Type alias in namespace #"%alib".
characters::xchar xchar
Type alias in namespace #"%alib".
TCString< TChar > BinLiteralPrefix
NumberFormatFlags Flags
The flag field.
TCString< TChar > NANLiteral
Defines what is written and parsed for double values that represent "not a number".
TCString< TChar > HexLiteralPrefix
TCString< TChar > OctLiteralPrefix
TCString< TChar > ExponentSeparator
TCString< TChar > INFLiteral
Defines what is written and parsed for infinite double values.