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