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