BigNumberByLeo/Src/Utils/Main.mqh

469 lines
15 KiB
MQL5
Raw Permalink Normal View History

2026-08-17 12:21:57 -05:00
//+------------------------------------------------------------------+
//| Main.mqh |
//| Copyright 2026, Niquel Mendoza |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Niquel Mendoza"
#property link "https://www.mql5.com"
#property strict
#ifndef BIGNUMBERSBYLEO_SRC_UTILS_MAIN_MQH
#define BIGNUMBERSBYLEO_SRC_UTILS_MAIN_MQH
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#include <TSN\\Tables\\Bits.mqh>
2026-08-17 12:21:57 -05:00
//+------------------------------------------------------------------+
//| |
2026-08-22 14:12:43 -05:00
//+------------------------------------------------------------------+
namespace TSN
{
2026-08-22 14:12:43 -05:00
//+------------------------------------------------------------------+
//| |
2026-08-17 12:21:57 -05:00
//+------------------------------------------------------------------+
class CNumberUtils
{
public:
CNumberUtils(void) {}
~CNumberUtils(void) {}
2026-08-19 11:09:16 -05:00
//--- Conversiones de bytes a hex string
2026-08-19 21:36:19 -05:00
static void BytesToHexUpper(const uchar& raw[], int t, string& out);
static void BytesToHexLower(const uchar& raw[], int t, string& out);
//--- Divisiones (d algortim)
static uint Div3by2(ulong a2, ulong a1, ulong a0, ulong v1, ulong v0, ulong &rem_out);
2026-08-21 21:01:14 -05:00
static ulong Div128to64(ulong hi, ulong lo, ulong d, ulong& res);
//--- mul 64*64
static void Mul64to128(const ulong a, const ulong b, ulong &hi, ulong &lo);
//--- To Bytes (numeros basicos)
template <typename TNumber>
static int SValueToBytesBE(const TNumber value, uchar& buf[], int w = 0, int k = -1);
template <typename TNumber>
static int SValueToBytesLE(const TNumber value, uchar& buf[], int w = 0, int k = -1);
2026-08-17 12:21:57 -05:00
};
2026-08-19 11:09:16 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
template <typename TNumber>
static int CNumberUtils::SValueToBytesLE(const TNumber value, uchar &buf[], int w = 0, int k = -1)
{
//---
if(k < 0 || k > sizeof(TNumber)) // clamp
k = sizeof(TNumber);
//---
if(k + w > ArraySize(buf))
ArrayResize(buf, k + w);
//---
union U
{
uchar bytes[sizeof(TNumber)];
TNumber value;
} rp;
rp.value = value;
//---
for(int i = 0; i < k; i++)
buf[w++] = rp.bytes[i];
//---
return w;
}
//+------------------------------------------------------------------+
template <typename TNumber>
static int CNumberUtils::SValueToBytesBE(const TNumber value, uchar &buf[], int w = 0, int k = -1)
{
//---
if(k == -2)
{
CLZ()
}
if(k < 0 || k > sizeof(TNumber)) // clamp
k = sizeof(TNumber);
//---
if(k + w > ArraySize(buf))
ArrayResize(buf, k + w);
//---
union U
{
uchar bytes[sizeof(TNumber)];
TNumber value;
} rp;
rp.value = value;
//---
for(int i = k - 1; i >= 0; i--)
buf[w++] = rp.bytes[i];
//---
return w;
}
2026-08-21 21:01:14 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#define NUMBERUTILS_BASE_ALG(LO, HI) \
const ulong lo_lo = a_lo * b_lo; \
const ulong hi_lo = a_hi * b_lo; \
const ulong lo_hi = a_lo * b_hi; \
const ulong hi_hi = a_hi * b_hi; \
const ulong inter = (lo_lo >> 32) + (uint)hi_lo + (uint)lo_hi; \
LO = (inter << 32) | (uint)lo_lo; \
HI = hi_hi + (hi_lo >> 32) + (lo_hi >> 32) + (inter >> 32); \
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
static void CNumberUtils::Mul64to128(const ulong a, const ulong b, ulong &hi, ulong &lo)
{
//---
const ulong a_lo = (uint)a;
const ulong a_hi = a >> 32;
const ulong b_lo = (uint)b;
const ulong b_hi = b >> 32;
//---
NUMBERUTILS_BASE_ALG(lo, hi)
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#define NUMBERUTILS_BASE32_UL (4294967296ULL)
//---
// division de un numero de 96 bits entre uno de 64 bits
// (pero tratamos a los numeors como base1<<32)
// A = Dividoendo de 96 bits
// V = Divisor de 64 bits
static uint CNumberUtils::Div3by2(ulong a2, ulong a1, ulong a0, ulong v1, ulong v0, ulong &rem_out)
{
// parte alta del numero
ulong num_hi = (a2 << 32) | a1;
// ahora estimoas incialemtne
ulong qhat;
// Ahora neceismots veirica rque la division de los altos no haya oveflow (tome 2 digitos o mas)
// Entonces tenemos esto
// a2a1a0 | v1v0
// sabemos primero que:
// si:
// a2 > v1
// el cociente sera grande y peude que de overflow.. pero esto hasta cierto punto exactmnte el punto de quiereb es ==
// de ahi en adlenteo con a2 < v1 entonces el reusltao cabe en un digito...
// Como noosotros garantizamos que a2 <= v1 por tema de que el resto temrina siendo el hi
// entonces solo nos queda comprar el unico caso de == en ese caso hay overflow.. o pued haberlo
// Para ese caso en concreto tipo ab/a tomeos un caso borde tipo 49/4 = 12 (truncado)
// ahi si pasa a mas de un digito el overflow seria de 10 % 10 = 2 seria el reusltado con overflow
// lo cual se aleja mucho de 12 como resultado demasido
// en cambio cno base-1 qeu es lo mas cercano a "base" que pódemos dar sin tener overflow tenemos un error de 3
// por eso.. lo elegimos
if(a2 == v1)
qhat = NUMBERUTILS_BASE32_UL - 1;
else
qhat = num_hi / v1;
// resto
ulong rhat = num_hi - qhat * v1;
// Ahora tenemos que corregir esa estimacion inicial qeu hicimos
// dado qeu no tuvimos en caunto los bajos
while(rhat < NUMBERUTILS_BASE32_UL && qhat * v0 > (rhat << 32) + a0)
{
qhat--;
rhat += v1;
}
// listo ahora si calculso el resto y el cociente finales
// multiplces el cociente qhat por el diviosr...
// a * b
// a = qhat
// b = full_v { (v1 << 32) | v0 } (V completo Divisor)
ulong prod_hi, prod_lo;
//--- mul64
{
//---
const ulong a_lo = (uint)qhat; //
const ulong a_hi = qhat >> 32;
const ulong full_v = (v1 << 32) | v0;
const ulong b_lo = (uint)full_v;
const ulong b_hi = full_v >> 32;
//---
2026-08-21 21:01:14 -05:00
NUMBERUTILS_BASE_ALG(prod_lo, prod_hi)
}
// Ahora Recontruyanos el dividendo A
ulong cur_hi = num_hi >> 32;
ulong cur_lo = (num_hi << 32) | a0;
// restemos (A - Qhat*Divisor)
ulong r_hi, r_lo;
//
// Sub128(ulong cur_hi, ulong cur_lo, ulong prod_hi, ulong prod_lo, ulong & r_hi, ulong & r_lo)
{
r_lo = cur_lo - prod_lo; // resta baja
//ulong borrow = (cur_lo < prod_lo); en caso sea menor hara overflow y neceismto quitarle a la resta alta.. eso
r_hi = cur_hi - prod_hi - (cur_lo < prod_lo);
}
// resto final (rhi como tal deberia de ser 0 por que a2<=v1)
rem_out = r_lo;
// cociente
return (uint)qhat;
}
2026-08-19 11:09:16 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
// hi=64,lo=64 dividendo de 128 bits
2026-08-21 21:01:14 -05:00
// d=64 divisor
2026-08-19 11:09:16 -05:00
// res=resto
//---
2026-08-21 21:01:14 -05:00
static ulong CNumberUtils::Div128to64(ulong hi, ulong lo, ulong d, ulong &res)
2026-08-19 11:09:16 -05:00
{
//---
2026-08-21 21:01:14 -05:00
if(d == 0)
2026-08-19 11:09:16 -05:00
{
// division por 0
res = 0;
return 0;
}
//---
// aqui no chekeamos si hi no es 0 dado qeu el uso de esta funcion ya se hace eso previo
// if (hi==0)
//--- Normalizacion
// Aqui la idea es "agrandar" los numeros
// la idea es que el algoritmo que utilizamos toma 2 digitios de del diviendo y los divide entre los altos del divisor
// luego con eso tenemos una esitmacion que corregiremos
// Esto funcioona como estimacion daod que si lo piensas
/*
Si didivdes digmoas te daras cuenta que si logras dividr los altos te da algo aproximado de lo qeu puede ser el resutlado
Por ejemplo aqui si dividies 100 /3 te da 33 una estimacion inicial.. pero oviamtne en el algoritmoi
solo podemos diividr 1/1 diogito asi que se hace una estimacion mas simple de sumar ambos digitios
y didviddr
153 | 3
*/
// Eso creo que es en parte luego deshemamos este escaldo de los datos
// Lo que si es que esto sirve para poder ejeuctar el algoritmo.. y simplifcair pasos y hacerlo mas rpaido
// y hacer que funcione esa esitmacion de tomar los atlos para estimar y corregir
//---
// Esto tambein es parte de el aglritmo de kruth, esto{ D=(B / L + 1) }
// pero hecho para div 128/64
// La idea es cumplir con >=b/2 b = 1<<64
// osea >= 1<<63 el minimo candidoa que cumple esto es 0x8000000000000000
// (100000000000....0) en binario... de ahi en adlente cumple
// asi que shifteamos el divisor hasta que tenga ese valor
int shift = 0; // cuants veces shiftemosa
while((d & 0x8000000000000000) == 0) // buscamos que el mas alto este activo
{
// si no lo esta vamos dezplazando
d <<= 1;
shift++;
}
// una vez lo tenga tenemos que normalizar lo qeu qeuda
ulong flow, hi_nor, lo_nor;
// uflow = el desborde que hi (dado que como shifteamos peude desbordar)
// hinor = hi normalizando
// lonor = lo normlizado
if(shift == 0) // ya estaba
{
hi_nor = hi;
lo_nor = lo;
flow = 0; // nada
}
else
{
// si dezplazamos
2026-08-19 21:36:19 -05:00
2026-08-19 11:09:16 -05:00
// los altos que salen
const int shr = 64 - shift;
2026-08-19 21:36:19 -05:00
flow = (hi >> shr);
2026-08-19 11:09:16 -05:00
// hi = [.....]
// tomamos los shift mejhores
// y los comabismo con el ov de lo
// [....][....]
// dezplaza << 2
// [..][[..][.. de lo]][[..][se perdiuern]]
hi_nor = (hi << shift) | (lo >> shr);
lo_nor = lo << shift;
}
// normllzaicion hecha
//--- Ahora si emepzamos la division
// Ahora partimos todo
// divisor en dos digitos
const ulong d_hi = d >> 32;
const ulong d_lo = d & 0xFFFFFFFF;
2026-08-19 11:09:16 -05:00
// dividendo = [flow | hi_n | lo_n]
const ulong dividendo_3 = hi_nor >> 32;
const ulong dividendo_2 = hi_nor & 0xFFFFFFFF;
const ulong dividendo_1 = lo_nor >> 32;
const ulong dividendo_0 = lo_nor & 0xFFFFFFFF;
//---- main
// La idea es ir diviendo como en esuclo de mayot a menor e ir bajaando restos
ulong r_hi = 0, r_lo = 0;
uint q4, q3, q2, q1, q0;
ulong rem;
/*
flow dividendo_3 dividendo_2 dividendo_1 dividendo_0 | d_hi d_lo
----------
1.
Dividors
rlo rhi flow entre dhi y dlo (parte alta)
*/
//---
q4 = Div3by2(r_hi, r_lo, flow, d_hi, d_lo, rem);
r_hi = rem >> 32;
r_lo = rem & 0xFFFFFFFF;
2026-08-19 11:09:16 -05:00
//---
q3 = Div3by2(r_hi, r_lo, dividendo_3, d_hi, d_lo, rem);
r_hi = rem >> 32;
r_lo = rem & 0xFFFFFFFF;
//---
q2 = Div3by2(r_hi, r_lo, dividendo_2, d_hi, d_lo, rem);
r_hi = rem >> 32;
r_lo = rem & 0xFFFFFFFF;
//---
q1 = Div3by2(r_hi, r_lo, dividendo_1, d_hi, d_lo, rem);
r_hi = rem >> 32;
r_lo = rem & 0xFFFFFFFF;
//---
q0 = Div3by2(r_hi, r_lo, dividendo_0, d_hi, d_lo, rem);
r_hi = rem >> 32;
r_lo = rem & 0xFFFFFFFF;
//---
// solo tomoas q1 y q0 (pro el tema de divisor > HI el resultado siempre cabra en 2 digitos)
const ulong cociente = ((ulong)q1 << 32) | (ulong)q0; // Q = q1*2^32 + q0
// ahoramos el resto full
const ulong resto_full = (r_hi << 32) | r_lo;
// ahora normalizamos
res = (shift == 0) ? resto_full : (resto_full >> shift);
2026-08-21 21:01:14 -05:00
//---
return cociente;
2026-08-19 21:36:19 -05:00
}
2026-08-19 11:09:16 -05:00
2026-08-19 21:36:19 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
/*
Un ejemplo internate del pro qeu esa noramllicion inincal es necesaira tomemos por ejemplo
la diivison de 490/40
Si vamos directo sin normlaizar tendmreos que
qhat= 9 (4==4)
rhat= (49 - (9*4)) = 49-36=13
y aqui ya se ve el erro de como 13 que pasa por mucho el "2" que sepodia tener sin normalizar
Y la division final todo ese proceso tambien se comntaine pro eelp el while no se corre (13<10 = false)
luego ya se ausme qeu es correcto por lo que hace un mulde
9 * 40 (4 | 0) = 360
luego
cur_hi = 4
curlo = 9 | 0
y restamos
(490 - 360) = 130
se toma la parte baja (30)
como resto=30
y qhat seria el cocinente 9
pero veriiando 9 * 30 = 270 esta super lejos de 490... error completo
Ahora con la normaliacion previa
nuestro factor D seria = (B / L+1) = 10 / 4+1 (L digitos mas singiacl del deivisor)
osea D=2
si escalamos
490*2=980
40*2=80
ahora liuego
qhat= 98/8 = 12 (aqui no es 9 adao que no son iguales)
rhat= (98 - (12 * 8)) = 98 - 96 = 2 (justo se cumple la teroia que no pasa 2...)
luego mul
12 * 80 = 960
ahora si
cur_hi = 9
curlo=80
980 - 960 = 20
con eso se temina y se cumple que R < Divisor asi que todo bien
Por eso es improate el tema de la normlaciino, en caso sborde como este donde se lleva al limite esto
nos topamos con este tipo de erorres que la aproxim de altos no da un reusltaod cercano
como el caso que anlsiodnde ab0/a0
Cero que pro eso esa aproxcim es util pro qeu noramlte cuiand divides numeros grandes el cociente por lo general es grande
no hay peleas con devcimales ni nada y luego solo corriges y tiens algo bueno en cambio si lo hicieras
sin onrlamliar te pelas con los deimcales enotnces creo que por ahi va el tema de la normalciion....
*/
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-08-19 21:36:19 -05:00
#define NUMBERUTILSBYLEO_ALG_BYTES \
{\
ushort buf[];\
ArrayResize(buf, t << 1);\
int w = 0;\
for(int i = 0; i < t; i++)\
{\
const uchar num = raw[i];\
buf[w++] = digitos[num >> 4]; \
buf[w++] = digitos[num & 15]; \
}\
out = ShortArrayToString(buf, 0, w); \
2026-08-19 11:09:16 -05:00
}
2026-08-19 21:36:19 -05:00
//+------------------------------------------------------------------+
static void CNumberUtils::BytesToHexUpper(const uchar &raw[], int t, string& out)
{
2026-08-17 12:21:57 -05:00
//---
2026-08-19 21:36:19 -05:00
static const ushort digitos[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
NUMBERUTILSBYLEO_ALG_BYTES
}
//+------------------------------------------------------------------+
static void CNumberUtils::BytesToHexLower(const uchar &raw[], int t, string& out)
{
//---
static const ushort digitos[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
NUMBERUTILSBYLEO_ALG_BYTES
}
2026-08-17 12:21:57 -05:00
}
#endif // BIGNUMBERSBYLEO_SRC_UTILS_MAIN_MQH