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