Dejo mi aporte: Una librería de clases con una clase y 2 métodos públicos: sumar() y multiplicar(), cada una acepta solo 2 parámetros de entrada. El límite teórico es de 8192 dígitos..... ¿alcanzará? :-)
Parece que algunos nos la pasamos bien ;-)
*-- ---------------------------------------------------------------------------------------------------------
*-- Autor.........................: Fernando D. Bozzo
*-- Creado el.....................: 23/08/2013 16:50
*-- Detalle.......................: LIBRERÍA DE FUNCIONES BÁSICAS DE CÁLCULO PARA CIFRAS MAYORES A 32 BITS
*-- Motivo........................: Superar la limitación de cálculo de FoxPro cuando se requiere trabajar
*-- con números mayores a 16 dígitos
*-- Requisitos....................: El punto decimal debe ser el internacional ==> '.'
*-- ---------------------------------------------------------------------------------------------------------
DEFINE CLASS xcalc AS CUSTOM
HIDDEN PROCEDURE NormalizarDatosEntrada( tcDato1, tcDato2, tnMaxLen, tnLenDato1, tnLenDato2, tnMaxEnteros, tnMaxDecimales ;
, tnPosDecimal1, tnPosDecimal2 )
lcDato1 = ALLTRIM(tcDato1)
lcDato2 = ALLTRIM(tcDato2)
THIS.obtenerCaracteristicasDeLosDatos( @lcDato1, @lcDato2, @tnMaxLen, @tnLenDato1, @tnLenDato2, @tnMaxEnteros ;
, @tnMaxDecimales, @tnPosDecimal1, @tnPosDecimal2 )
tcDato1 = PADL( LEFT(lcDato1, tnPosDecimal1-1), tnMaxEnteros, '0' ) + '.' ;
+ PADR( SUBSTR(lcDato1, tnPosDecimal1+1), tnMaxDecimales, '0' )
tcDato2 = PADL( LEFT(lcDato2, tnPosDecimal2-1), tnMaxEnteros, '0' ) + '.' ;
+ PADR( SUBSTR(lcDato2, tnPosDecimal2+1), tnMaxDecimales, '0' )
ENDPROC
HIDDEN PROCEDURE obtenerCaracteristicasDeLosDatos( tcDato1, tcDato2, tnMaxLen, tnLenDato1, tnLenDato2, tnMaxEnteros, tnMaxDecimales ;
, tnPosDecimal1, tnPosDecimal2 )
*-- Esta normalización se hace para acomodar las cifras y hacer que coincida
*-- la columna del punto decimal.
LOCAL lnSubtotalColumna, lcSubtotal, lcSubtotalColumna, lnMeLlevo, lcDato1, lcDato2, lcDigito1 ;
, lnDigito1, lnDigito2, lnLenSubtotalColumna
STORE 0 TO lnMeLlevo, tnMaxLen, lnDigito1, lnDigito2 ;
, tnMaxEnteros, tnMaxDecimales, tnPosDecimal1, tnPosDecimal2, tnLenDato1, tnLenDato2
STORE '' TO lcSubtotal, lcDato1, lcDato2
lcDato1 = ALLTRIM(tcDato1)
lcDato2 = ALLTRIM(tcDato2)
tnLenDato1 = LEN(lcDato1)
tnLenDato2 = LEN(lcDato2)
tnPosDecimal1 = AT('.', lcDato1)
tnPosDecimal2 = AT('.', lcDato2)
IF tnPosDecimal1 = 0
tnPosDecimal1 = tnLenDato1 + 1
ENDIF
IF tnPosDecimal2 = 0
tnPosDecimal2 = tnLenDato2 + 1
ENDIF
tnMaxEnteros = MAX( tnPosDecimal1, tnPosDecimal2 ) - 1
tnMaxDecimales = MAX( tnLenDato1 - tnPosDecimal1, tnLenDato2 - tnPosDecimal2, 0 )
tnMaxLen = tnMaxEnteros + 1 + tnMaxDecimales
RETURN
ENDPROC
HIDDEN PROCEDURE calcular_MeLlevo( tcSubtotal, tnSubtotalColumna, tnMeLlevo )
LOCAL lcSubtotalColumna, lnLenSubtotalColumna
*-- Si es mayor a 9, separo las unidades y "me llevo" lo demás
IF tnSubtotalColumna > 9
lcSubtotalColumna = TRANSFORM( tnSubtotalColumna )
lnLenSubtotalColumna = LEN( lcSubtotalColumna )
tnMeLlevo = INT( VAL( LEFT( lcSubtotalColumna, lnLenSubtotalColumna - 1 ) ) )
tcSubtotal = RIGHT( lcSubtotalColumna, 1 ) + tcSubtotal
ELSE && Es <= 9: Tomo las unidades
tnMeLlevo = 0
tcSubtotal = TRANSFORM( tnSubtotalColumna ) + tcSubtotal
ENDIF
ENDPROC
HIDDEN PROCEDURE normalizarDatosSalida( tcTotal )
*-- Se quitan los ceros sobrantes
LOCAL lnPosSignificativa, lnPosDecimal, lnLenTotal, llEncontrado
*-- Normalizo el punto decimal
IF RIGHT(tcTotal,1) = '.'
tcTotal = LEFT( tcTotal, LEN(tcTotal)-1 )
ENDIF
THIS.obtenerLenTotalYPosDecimal( tcTotal, @lnLenTotal, @lnPosDecimal )
*-- Analizo los enteros
llEncontrado = .F.
FOR lnPosSignificativa = 1 TO lnPosDecimal - 1
IF SUBSTR( tcTotal, lnPosSignificativa, 1 ) # '0'
llEncontrado = .T.
EXIT
ENDIF
ENDFOR
IF llEncontrado AND lnPosSignificativa > 1
tcTotal = SUBSTR( tcTotal, lnPosSignificativa )
THIS.obtenerLenTotalYPosDecimal( tcTotal, @lnLenTotal, @lnPosDecimal )
ENDIF
*-- Analizo los decimales
IF lnPosDecimal < lnLenTotal
llEncontrado = .F.
FOR lnPosSignificativa = lnLenTotal TO lnPosDecimal STEP -1
IF SUBSTR( tcTotal, lnPosSignificativa, 1 ) # '0'
llEncontrado = .T.
EXIT
ENDIF
ENDFOR
IF llEncontrado AND lnPosSignificativa > 1
tcTotal = LEFT( tcTotal, lnPosSignificativa )
ENDIF
ENDIF
ENDPROC
HIDDEN PROCEDURE obtenerLenTotalYPosDecimal( tcTotal, tnLenTotal, tnPosDecimal )
tnLenTotal = LEN( tcTotal )
tnPosDecimal = AT( '.', tcTotal )
IF tnPosDecimal = 0
tnPosDecimal = tnLenTotal + 1
ENDIF
ENDPROC
FUNCTION sumar( tcDato1, tcDato2 )
*--
LOCAL lnSubtotalColumna, lcSubtotal, lcSubtotalColumna, lnMeLlevo, lcDato1, lcDato2, lcDigito1 ;
, lnMaxLen, lnDigito1, lnDigito2, lnLenSubtotalColumna
STORE 0 TO lnMeLlevo, lnMaxLen, lnDigito1, lnDigito2, lnSubtotalColumna, lnLenSubtotalColumna
STORE '' TO lcSubtotalColumna, lcSubtotal, lcDato1, lcDato2
lcDato1 = ALLTRIM(tcDato1)
lcDato2 = ALLTRIM(tcDato2)
THIS.NormalizarDatosEntrada( @lcDato1, @lcDato2, @lnMaxLen )
*-- Voy sumando los dígitos desde la última columna hacia la izquierda, por cada columna
FOR I = lnMaxLen TO 1 STEP -1
lcDigito1 = SUBSTR( lcDato1, I, 1 )
IF lcDigito1 = '.'
*-- Bajo el punto decimal
lcSubtotal = '.' + lcSubtotal
ELSE
lnDigito1 = INT( VAL( lcDigito1 ) )
lnDigito2 = INT( VAL( SUBSTR( lcDato2, I, 1 ) ) )
lnSubtotalColumna = lnDigito1 + lnDigito2 + lnMeLlevo && Sumo la columna y sumo el "me llevo"
THIS.calcular_MeLlevo( @lcSubtotal, @lnSubtotalColumna, @lnMeLlevo )
ENDIF
ENDFOR
THIS.normalizarDatosSalida( @lcSubtotal )
RETURN lcSubtotal
ENDFUNC
FUNCTION multiplicar( tcDato1, tcDato2 )
*--
LOCAL lnSubtotalColumna, lcSubtotal, lcSubtotalColumna, lnMeLlevo, lcDato1, lcDato2, lcDigito1, lcDigito2, lnLenTotal ;
, lnMaxLen, lnDigito1, lnDigito2, lnLenSubtotalColumna, lnFilaSubtotal, lnPosDecimal ;
, lnLenDato1, lnLenDato2, lnMaxEnteros, lnMaxDecimales, lcTotal, lnPosDecimal1, lnPosDecimal2, lnMoverComaAIzq
STORE 0 TO lnMeLlevo, lnMaxLen, lnDigito1, lnDigito2, lnSubtotalColumna, lnLenSubtotalColumna, lnPosDecimal ;
, lnFilaSubtotal, lnLenDato1, lnLenDato2, lnMaxEnteros, lnMaxDecimales, lnPosDecimal1, lnPosDecimal2, lnLenTotal
STORE '' TO lcSubtotalColumna, lcSubtotal, lcDato1, lcDato2, lcTotal
lcDato1 = ALLTRIM(tcDato1)
lcDato2 = ALLTRIM(tcDato2)
THIS.obtenerCaracteristicasDeLosDatos( @lcDato1, @lcDato2, @lnMaxLen, @lnLenDato1, @lnLenDato2, @lnMaxEnteros, @lnMaxDecimales ;
, @lnPosDecimal1, @lnPosDecimal2 )
*-- Voy multiplicando los dígitos desde la última columna hacia la izquierda, por cada columna
FOR X = lnLenDato2 TO 1 STEP -1
lnMeLlevo = 0
lcSubtotal = ''
lcDigito2 = SUBSTR( lcDato2, X, 1 )
IF lcDigito2 # '.'
lnFilaSubtotal = lnFilaSubtotal + 1
IF lcDigito2 # '0'
FOR I = lnLenDato1 TO 1 STEP -1
lcDigito1 = SUBSTR( lcDato1, I, 1 )
IF lcDigito1 = '.'
*-- Salteo el punto decimal
ELSE
lnDigito1 = INT( VAL( lcDigito1 ) )
lnDigito2 = INT( VAL( lcDigito2 ) )
lnSubtotalColumna = lnDigito1 * lnDigito2 + lnMeLlevo && Multiplico la columna y sumo el "me llevo"
THIS.calcular_MeLlevo( @lcSubtotal, @lnSubtotalColumna, @lnMeLlevo )
ENDIF
ENDFOR
lcTotal = THIS.sumar( lcTotal, lcSubtotal + REPLICATE( '0', lnFilaSubtotal - 1 ) )
ENDIF
ENDIF
ENDFOR
lnMoverComaAIzq = lnLenDato1 - lnPosDecimal1 + lnLenDato2 - lnPosDecimal2
lnLenTotal = LEN( lcTotal )
lnPosDecimal = AT( '.', lcTotal )
IF lnPosDecimal = 0
lcTotal = LEFT( lcTotal, lnLenTotal - lnMoverComaAIzq ) + '.' + SUBSTR( lcTotal, lnLenTotal - lnMoverComaAIzq + 1 )
ELSE
lnPosDecimal = lnPosDecimal - lnMoverComaAIzq
lcTotal = CHRTRAN( lcTotal, '.', '' )
lcTotal = LEFT( lcTotal, lnPosDecimal - 1 ) + '.' + SUBSTR( lcTotal, lnPosDecimal + 1 )
ENDIF
THIS.normalizarDatosSalida( @lcTotal )
RETURN lcTotal
ENDFUNC
ENDDEFINE