2026-10-09 12:34:15 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| Conv.mqh |
|
| | | //| Copyright 2026, Niquel Mendoza. |
|
| | | //| https://www.mql5.com |
|
| | | //+------------------------------------------------------------------+
|
| | | #property copyright "Copyright 2026, Niquel Mendoza."
|
| | | #property link "https://www.mql5.com"
|
| | | #property strict
|
| | |
|
2026-10-09 12:56:38 -05:00 | | | #ifndef TSNSTD_SRC_STRING_CONV_MQH
|
| | | #define TSNSTD_SRC_STRING_CONV_MQH
|
2026-10-09 12:34:15 -05:00 | | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | #include "ConvDef\\Dbl.mqh"
|
| | | #include <TSN\\Tables\\DPairs.mqh>
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | namespace TSN
|
| | | {
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | class CStringConvert
|
| | | {
|
| | | private:
|
| | |
|
| | |
|
| | | public:
|
| | | CStringConvert(void) {}
|
| | | ~CStringConvert(void) {}
|
| | |
|
| | | //---
|
| | | // static int s_len;
|
| | |
|
| | | //--- to value genericos
|
| | | template <typename T>
|
| | | static void ToValueNSigned(const string& str, int& pos, T& v, const int s_len);
|
| | | template <typename T>
|
| | | static void ToValueNUnsigned(const string& str, int& pos, T& v, const int s_len);
|
| | |
|
| | |
|
| | | //--- to number signed
|
| | | static __forceinline void ToValue(const string& str, int& pos, long& v, const int s_len) { ToValueNSigned(str, pos, v, s_len); }
|
| | | static __forceinline void ToValue(const string& str, int& pos, int& v, const int s_len) { ToValueNSigned(str, pos, v, s_len); }
|
| | | static __forceinline void ToValue(const string& str, int& pos, short& v, const int s_len) { ToValueNSigned(str, pos, v, s_len); }
|
| | |
|
| | | //--- ungisend
|
| | | static __forceinline void ToValue(const string& str, int& pos, ulong& v, const int s_len) { ToValueNUnsigned(str, pos, v, s_len); }
|
| | | static __forceinline void ToValue(const string& str, int& pos, uint& v, const int s_len) { ToValueNUnsigned(str, pos, v, s_len); }
|
| | | static __forceinline void ToValue(const string& str, int& pos, ushort& v, const int s_len) { ToValueNUnsigned(str, pos, v, s_len); }
|
| | | static __forceinline void ToValue(const string& str, int& pos, uchar& v, const int s_len) { ToValueNUnsigned(str, pos, v, s_len); }
|
| | |
|
| | |
|
| | | //---
|
| | | static void ToValue(const string& str, int& pos, double& val, const int s_len);
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | static void CStringConvert::ToValue(const string &str, int &pos, double &val, const int s_len)
|
| | | {
|
| | | //--- Inicial
|
| | | val = 0.0;
|
| | | ulong v = 0;
|
| | | uchar ch = (uchar)str[pos] ^ '0';
|
| | | long mask = 0;
|
| | | int lock = pos;
|
| | |
|
| | | //--- primer valor
|
| | | if(ch > 9)
|
| | | {
|
| | | switch(ch)
|
| | | {
|
| | | case('+' ^ '0'):
|
| | | {
|
| | | if(++pos >= s_len)
|
| | | return;
|
| | | lock++;
|
| | | if((ch = (uchar)str[pos] ^ '0') > 9)
|
| | | {
|
| | | if(!STRCONV_DECX_CH_IS_PDEC(ch)) // si en caso la mascara no es -1
|
| | | return;
|
| | | ch = 0;
|
| | | pos = 0; // justo en +
|
| | | }
|
| | | break;
|
| | | }
|
| | | case(',' ^ '0'):
|
| | | return;
|
| | | case('-' ^ '0'):
|
| | | {
|
| | | if(++pos >= s_len)
|
| | | return;
|
| | | mask = -1;
|
| | | if((ch = (uchar)str[pos] ^ '0') > 9)
|
| | | {
|
| | | if(!STRCONV_DECX_CH_IS_PDEC(ch)) // si en caso la mascara no es -1
|
| | | return;
|
| | | ch = 0;
|
| | | pos = 0; // justo en -
|
| | | }
|
| | | break;
|
| | | }
|
| | | case('.' ^ '0'):
|
| | | {
|
| | | ch = 0;
|
| | | pos = -1;
|
| | | break;
|
| | | }
|
| | | default:
|
| | | return; // invalido
|
| | | }
|
| | | }
|
| | |
|
| | | //--- Enteros
|
| | | while(true)
|
| | | {
|
| | | //--- 20 iteraciones maximo (unrolling)
|
| | | STRCONV_TO_VALUE_IT(v, ch)
|
| | | if(!v) // leading zero inicial (no cuenta)
|
| | | {
|
| | | do
|
| | | {
|
| | | if(++pos >= s_len)
|
| | | return;
|
| | | }
|
| | | while((ch = (uchar)str[pos]) == '0');
|
| | | if((ch ^= '0') > 9) // punto o e\E.
|
| | | {
|
| | | lock = pos - 1; // antes de dicho valor
|
| | | break; // sale
|
| | | }
|
| | | lock = pos; // justo en este valor
|
| | | //--- iteracion extra para empzar bien
|
| | | STRCONV_TO_VALUE_IT(v, ch)
|
| | | }
|
| | | STRCONV_U18_IT(v, ch)
|
| | |
|
| | | //--- apartir de aqui ya no impota la parte decimal asi que "truncamos a 19 justo como lo tenemos"
|
| | | const int ps = pos; // incio de la parte truncada
|
| | | while(++pos < s_len && ((uchar)str[pos] ^ '0') < 10) {}
|
| | | if(STRCONV_DECX_CH_IS_DEC(ch)) // es decimal en todo caso..
|
| | | break;
|
| | |
|
| | | //--- e base
|
| | | int e = pos - ps;
|
| | | ulong vf; // vfinal
|
| | | int ef; // efinal
|
| | | CDoubleParser::ComputeDbl(e, v, vf, ef);
|
| | | //--- e +1
|
| | | ulong p1_v; // pv
|
| | | int p1_e; // ep
|
| | | CDoubleParser::ComputeDbl(e, v + 1, p1_v, p1_e);
|
| | | //---
|
| | | if(p1_v == vf && ef == p1_e)
|
| | | {
|
| | | STRCONV_DOUBLE_MAKE_DBL_WMASK(ef, vf, mask, val)
|
| | | }
|
| | | else // fallback a nativo (como nuestro big integer)
|
| | | val = StringToDouble(str.Substr(lock, (pos - lock)));
|
| | | return;
|
| | | }
|
| | | //--- Garantia: Si llega hasta aqui es que se cumple:
|
| | | // 1. v en caso llege con ov si o si es decimal (asi qeu la ruta 2 de v tal cual esta bien), si no tiene ov
|
| | | // puede ser o dec o ent
|
| | | //--- Ahora double
|
| | | if(STRCONV_DECX_CH_IS_DEC(ch)) // decimal aqui empezamos...
|
| | | {
|
| | | long exponente = 0;
|
| | | int digitos = (pos - lock); // ojo tal cual luego hay que sumar mask
|
| | | // int k = 0;
|
| | |
|
| | | //---
|
| | | switch(0)
|
| | | {
|
| | | default:
|
| | | {
|
| | | if(STRCONV_DECX_CH_IS_PDEC(ch))
|
| | | {
|
| | | if(++pos >= s_len || (ch = (uchar)str[pos] ^ '0') > 9)
|
| | | break;
|
| | | //---
|
| | | int ds = pos;
|
| | | while(true)
|
| | | {
|
| | | STRCONV_TO_VALUE_IT(v, ch)
|
| | | if(!v) // patron probale: 0.05
|
| | | {
|
| | | if(ch == 0)
|
| | | {
|
| | | while(++pos < s_len && (ch = (uchar)str[pos]) == '0') {}
|
| | | digitos = (ds - pos); // calculamos
|
| | | if(!(ch ^= '0') || ch > 9) // sale por 0 o mayor a 9
|
| | | break;
|
| | | }
|
| | | else
|
| | | digitos = (ds - pos);
|
| | | // otra mas por que se perdio un valor para sumar los 19 digitos
|
| | | STRCONV_TO_VALUE_IT(v, ch)
|
| | | }
|
| | | STRCONV_U18_IT(v, ch)
|
| | | STRCONV_SKIP_NUMBER
|
| | | break;
|
| | | }
|
| | | //---
|
| | | ds = (pos - ds);
|
| | | exponente -= ds;
|
| | | digitos += ds;
|
| | | }
|
| | | if(STRCONV_DECX_CH_IS_E(ch))
|
| | | {
|
| | | if(++pos >= s_len)
|
| | | break;
|
| | | //---
|
| | | bool exp_positivo = true;
|
| | | ch = (uchar)str[pos] ^ '0';
|
| | | if(ch > 9)
|
| | | {
|
| | | if(STRCONV_DECX_CH_IS_MINUS(ch))
|
| | | exp_positivo = false;
|
| | | if(++pos >= s_len || (ch = (uchar)str[pos] ^ '0') > 9)
|
| | | break; // luego del +\- no hay un numero
|
| | | }
|
| | | int expo = 0;
|
| | | //---
|
| | | while(true) // siguimos acomulando en v
|
| | | {
|
| | | STRCONV_TO_VALUE_IT(expo, ch)
|
| | | if(expo == 0) // leading zeros.. ahora mismo aun hay digitos pero el primero es 0.. saltamos
|
| | | {
|
| | | STRCONV_LEADING_ZEROS_SKIP_STRICT(break)
|
| | | }
|
| | | STRCONV_8_IT(expo, ch)
|
| | | STRCONV_TOV_ALG(expo, ch)
|
| | | if(++pos >= s_len || (ch = (uchar)str[pos] ^ '0') > 9)
|
| | | {
|
| | | if(expo > 0) // es valido
|
| | | break; // no ov
|
| | | }
|
| | | else // sigue saltamos todo lo que hya
|
| | | {
|
| | | STRCONV_SKIP_NUMBER
|
| | | }
|
| | | // satura..
|
| | | expo = INT_MAX;
|
| | | break; //
|
| | | }
|
| | | exponente += exp_positivo ? expo : -expo;
|
| | | }
|
| | | break;
|
| | | }
|
| | | }
|
| | |
|
| | | //--- Fase 3 final...
|
| | | digitos += (int)mask;
|
| | | if(STRCONV_DOUBLE_SAFE_DBL(digitos, exponente)) // clinger fast path
|
| | | {
|
| | | __UDoubleToLong u;
|
| | | u.d = STRCONV_TO_DOUBLE_ALG(exponente, v);
|
| | | u.l ^= (mask & (1LL << 63));
|
| | | val = u.d;
|
| | | }
|
| | | else
|
| | | {
|
| | | //---
|
| | | if(digitos < 20) // lemire -mid path primero vemos si se puede aun
|
| | | {
|
| | | int e = int(exponente);
|
| | | CDoubleParser::ComputeDbl(e, v, v, e);
|
| | | STRCONV_DOUBLE_MAKE_DBL_WMASK(e, v, mask, val)
|
| | | return;
|
| | | }
|
| | |
|
| | | //--- slow path big integer.
|
| | | val = StringToDouble(str.Substr(lock, (pos - lock)));
|
| | | }
|
| | | }
|
| | | else
|
| | | {
|
| | | __UDoubleToLong u;
|
| | | u.d = double(v);
|
| | | u.l ^= (mask & (1LL << 63));
|
| | | val = u.d;
|
| | | }
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | // 1. No se acepta ws adelante
|
| | | // 2. Se asume como minimo un digiito valid (len minimo 1 por parte del caller)
|
| | | // 3. Se para justo en el primer char no numero
|
| | | // 4. solo tipos nativos (menoes a 64 bits.. y solo se acepta 1\2\4\8 bytes)
|
| | | // osea long,ulong,int,uint,char,uchar,short,ushort,color,datetime (todos los numeros nativos)
|
| | |
|
| | | //---
|
| | | template <typename T>
|
| | | static void CStringConvert::ToValueNSigned(const string &str, int& pos, T& v, const int s_len)
|
| | | {
|
| | | //--- Inicial
|
| | | v = 0;
|
| | | uchar ch = (uchar)str[pos] ^ '0';
|
| | | T mask = 0;
|
| | |
|
| | | //--- primer valor (invalido)
|
| | | if(ch > 9)
|
| | | {
|
| | | if(++pos >= s_len)
|
| | | return;
|
| | | if(ch == STRCONV_ZERO_XOR_MENOS)
|
| | | mask = -1;
|
| | | if((ch = (uchar)str[pos] ^ '0') > 9)
|
| | | return;
|
| | | }
|
| | |
|
| | | //--- leading zeros
|
| | | if(!ch)
|
| | | {
|
| | | STRCONV_LEADING_ZEROS_SKIP_STRICT(return)
|
| | | }
|
| | |
|
| | | //--- ahora si
|
| | | const int lock = pos;
|
| | | // bucle sin ramas ni nada..
|
| | | while(true)
|
| | | {
|
| | | //--- 19 iteraciones maximo (unrolling)
|
| | | STRCONV_U18_IT(v, ch)
|
| | | // la iteracion 19.. tambien se salva
|
| | | STRCONV_TO_VALUE_IT(v, ch)
|
| | | // si logra pasar ya es que aun sigue siendo valido... pero ya esto es mas de 20 digitos ov seguro
|
| | |
|
| | | //---
|
| | | // la idea es dejar el pos hasta donde podmoas.. ya aqui ya no acomularemso v no tiene caso.
|
| | | STRCONV_SKIP_NUMBER
|
| | |
|
| | | //--- overflow o underf. seguro (caulquier caso)
|
| | | v = NUMBERUTILS_MAX_VALUE_OFT_SIGNED(T) ^ mask;
|
| | |
|
| | | //---
|
| | | return;
|
| | | }
|
| | |
|
| | | //--- Comportamiento igual que el original saturamos..
|
| | | if((pos - lock) >= STRCONV_DIGITOS_SIGNED(T)) // en caso sea mayor o igual.. hay posible ov
|
| | | {
|
| | | const int digits = (pos - lock); // calculamos el numero de digitos
|
| | | if(digits == STRCONV_DIGITOS_SIGNED(T)) // maximo.. posible revisamo
|
| | | {
|
| | | ch = (uchar)str[lock] ^ '0';
|
| | | const bool sat = (ch > STRCONV_I_FIRST_DIG(T) || (ch == STRCONV_I_FIRST_DIG(T) && v < 0));
|
| | | if(!sat)
|
| | | return;
|
| | | }
|
| | | // Se pasa.. entonces si o si hay ov... saturamos..
|
| | | //---
|
| | | v = NUMBERUTILS_MAX_VALUE_OFT_SIGNED(T) ^ mask;
|
| | | return; // sale.
|
| | | }
|
| | |
|
| | | //---
|
| | | v ^= mask;
|
| | | v -= mask;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | template <typename T>
|
| | | static void CStringConvert::ToValueNUnsigned(const string& str, int& pos, T& v, const int s_len)
|
| | | {
|
| | | //--- Inicial
|
| | | v = 0;
|
| | | uchar ch = (uchar)str[pos] ^ '0';
|
| | |
|
| | | //--- primer valor (invalido)
|
| | | if(ch > 9)
|
| | | return;
|
| | |
|
| | | //--- leading zeros
|
| | | if(!ch)
|
| | | {
|
| | | STRCONV_LEADING_ZEROS_SKIP_STRICT(return)
|
| | | }
|
| | |
|
| | | //--- ahora si
|
| | | const int lock = pos;
|
| | | while(true)
|
| | | {
|
| | | //--- 20 iteraciones maximo (unrolling)
|
| | | STRCONV_U18_IT(v, ch)
|
| | | STRCONV_TO_VALUE_IT(v, ch)
|
| | | // la iteracion 20.. tambien se salva
|
| | | STRCONV_TO_VALUE_IT(v, ch)
|
| | | // si logra pasar ya es que aun sigue siendo valido... pero ya esto es mas de 20 digitos ov seguro
|
| | |
|
| | | //---
|
| | | // la idea es dejar el pos hasta donde podmoas.. ya aqui ya no acomularemso v no tiene caso.
|
| | | STRCONV_SKIP_NUMBER
|
| | |
|
| | | //--- overflow seguro (caulquier caso)
|
| | | v = NUMBERUTILS_MAX_VALUE_OFT_USIGNED(T);
|
| | | return;
|
| | | }
|
| | |
|
| | | //--- Comportamiento igual que el original saturamos..
|
| | | if((pos - lock) >= STRCONV_DIGITOS_UNSIGNED(T)) // en caso sea mayor o igual.. hay posible ov
|
| | | {
|
| | | const int digits = (pos - lock); // calculamos el numero de digitos
|
| | | if(digits == STRCONV_DIGITOS_UNSIGNED(T)) // maximo.. posible revisamo
|
| | | {
|
| | | ch = (uchar)str[lock] ^ '0';
|
| | | const bool sat = (ch > STRCONV_U_FIRST_DIG(T) || (ch == STRCONV_U_FIRST_DIG(T) && v < (T)NUMBERUTILS_MAX_VALUE_OFT_SIGNED(T)));
|
| | | if(!sat)
|
| | | return;
|
| | | }
|
| | | // Se pasa.. entonces si o si hay ov... saturamos..
|
| | | //---
|
| | | v = NUMBERUTILS_MAX_VALUE_OFT_USIGNED(T);
|
| | | }
|
| | | }
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Maximo numero de digitos |
|
| | | //+------------------------------------------------------------------+
|
| | | //--- incluye el signo para i\l (+1)
|
| | | #define MQLARTICLES_CONV_MAXDIG_I (11)
|
| | | #define MQLARTICLES_CONV_MAXDIG_L (20)
|
| | |
|
| | | //---
|
| | | #define MQLARTICLES_CONV_MAXDIG_UL (20)
|
| | |
|
| | | //---
|
| | | // 16 + 1 + 1 + 4 (2 a la 64 divido entre 10 a la 16..)
|
| | | // 6 si o si mas digitos
|
| | | #define MQLARTICLES_CONV_MAXDIG_DBL(dig) (6 + dig)
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | class CValueToBuffer
|
| | | {
|
| | | public:
|
| | | CValueToBuffer(void) {}
|
| | | ~CValueToBuffer(void) {}
|
| | |
|
| | | //--- convertir enteros
|
| | | //- unsigned
|
| | | template <typename TUValue>
|
| | | static void _ToValUSigned(TUValue uvalue, uchar& in[], int pos);
|
| | |
|
| | | //---
|
| | | static void ToValue(double v, uchar& in[], int& w, uchar digits);
|
| | |
|
| | | //---
|
| | | static string ULLToStr(ulong uvalue);
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | static void CValueToBuffer::ToValue(double v, uchar &in[], int &w, uchar digits)
|
| | | {
|
| | | //--- signo
|
| | | if(v < 0)
|
| | | {
|
| | | in[w++] = '-';
|
| | | v = -v;
|
| | | }
|
| | |
|
| | | //---
|
| | | const double factor = g_potencias_de_10[digits]; // potencias positivas
|
| | | const ulong escalado = (ulong)MathRound(v * factor);
|
| | | const ulong factor_u = (ulong)factor;
|
| | | ulong ent = escalado / factor_u;
|
| | | ulong dec = escalado - ent * factor_u;
|
| | |
|
| | | //---
|
| | | {
|
| | | int pos = w + CBitTricks::CountDigitsBinary(ent);
|
| | | w = pos;
|
| | |
|
| | | //---
|
| | | while(ent >= 100)
|
| | | {
|
| | | const ulong q = ent / 100ULL;
|
| | | const ulong idx = (ent - q * 100ULL) << 1;
|
| | | ent = q;
|
| | | in[--pos] = g_tsn_digit_pairs[idx + 1];
|
| | | in[--pos] = g_tsn_digit_pairs[idx];
|
| | | }
|
| | | if(ent >= 10)
|
| | | {
|
| | | const ulong idx = ent << 1ULL;
|
| | | in[--pos] = g_tsn_digit_pairs[idx + 1];
|
| | | in[--pos] = g_tsn_digit_pairs[idx];
|
| | | }
|
| | | else
|
| | | in[--pos] = uchar('0' + ent);
|
| | | }
|
| | |
|
| | | //---
|
| | | in[w++] = '.';
|
| | |
|
| | | //---
|
| | | int pos = w + digits;
|
| | | w = pos;
|
| | |
|
| | | //---
|
| | | int i = 0;
|
| | | for(; i + 1 < digits; i += 2)
|
| | | {
|
| | | const ulong q = dec / 100ULL;
|
| | | const ulong idx = (dec - q * 100ULL) << 1;
|
| | | dec = q;
|
| | | in[--pos] = g_tsn_digit_pairs[idx + 1];
|
| | | in[--pos] = g_tsn_digit_pairs[idx];
|
| | | }
|
| | | if(i < digits)
|
| | | in[--pos] = uchar('0' + (dec % 10ULL));
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | // helper
|
| | | #define STRCONV_VALTOBUFFER_USIGNED(uvalue, in, w) \
|
| | | w += CBitTricks::CountDigitsBinary(uvalue); \
|
| | | TSN::CValueToBuffer::_ToValUSigned(uvalue, in, w);
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | #define STRCONV_VALTOBUFFER_SIGNED(value, in, w, TUValue, VAR) \
|
| | | TUValue VAR; \
|
| | | if(value < 0) \
|
| | | { \
|
| | | in[w++] = '-'; \
|
| | | VAR = TUValue(-(value + 1)) + 1; \
|
| | | } \
|
| | | else \
|
| | | VAR = TUValue(value); \
|
| | | STRCONV_VALTOBUFFER_USIGNED(VAR, in, w)
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | // asume pos ya aumentado si no puede ser algo consistente (seria raro no?¿) use los macros
|
| | | template <typename TUValue>
|
| | | static void CValueToBuffer::_ToValUSigned(TUValue uvalue, uchar &in[], int pos)
|
| | | {
|
| | | //---
|
| | | while(uvalue >= 100)
|
| | | {
|
| | | const TUValue prev = uvalue;
|
| | | uvalue /= 100U;
|
| | | const TUValue idx = (prev - (uvalue * 100U)) << 1;
|
| | | in[--pos] = g_tsn_digit_pairs[idx + 1];
|
| | | in[--pos] = g_tsn_digit_pairs[idx];
|
| | | }
|
| | | if(uvalue >= 10)
|
| | | {
|
| | | const TUValue idx = uvalue << 1;
|
| | | in[--pos] = g_tsn_digit_pairs[idx + 1];
|
| | | in[--pos] = g_tsn_digit_pairs[idx];
|
| | | }
|
| | | else
|
| | | in[--pos] = uchar('0' + uvalue);
|
| | | }
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | // Extra para string
|
| | | static string CValueToBuffer::ULLToStr(ulong uvalue)
|
| | | {
|
| | | //---
|
| | | int pos = CBitTricks::CountDigitsBinary(uvalue);
|
| | | string str;
|
| | | StringInit(str, pos, ' ');
|
| | |
|
| | | //---
|
| | | while(uvalue >= 100)
|
| | | {
|
| | | const ulong prev = uvalue;
|
| | | uvalue /= 100U;
|
| | | const ulong idx = (prev - (uvalue * 100U)) << 1;
|
| | | str.SetChar(--pos, g_tsn_digit_pairs[idx + 1]);
|
| | | str.SetChar(--pos, g_tsn_digit_pairs[idx]);
|
| | | }
|
| | | if(uvalue >= 10)
|
| | | {
|
| | | const ulong idx = uvalue << 1;
|
| | | str.SetChar(--pos, g_tsn_digit_pairs[idx + 1]);
|
| | | str.SetChar(--pos, g_tsn_digit_pairs[idx]);
|
| | | }
|
| | | else
|
| | | str.SetChar(--pos, uchar('0' + uvalue));
|
| | |
|
| | | //---
|
| | | return str;
|
| | | }
|
| | |
|
| | | }
|
| | |
|
| | | //---
|
2026-10-09 12:56:38 -05:00 | | | #endif // TSNSTD_SRC_STRING_CONV_MQH
|
2026-10-09 12:34:15 -05:00 | | | //+------------------------------------------------------------------+
|