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