2010-02-09 9 views
6

Recibo un número entero que representa una cantidad en dólares en denominaciones fraccionarias. Me gustaría un algoritmo que pueda agregar esos números sin analizarlos y convertirlos en dobles o decimales.Contaje fraccional Vía enteros

Por ejemplo, recibo el número entero 50155, que significa 50 y 15,5/32 dólares. Luego recibo 10210, que es 10 y 21/32 dólares. Así 50 15,5/32 + 10 21/32 = 61 4,5/32, así:

50155 + 10210 = 61045

Una vez más, quiero evitar esto:

int a = 50155; 
int b = a/1000; 
float c = a % 1000; 
float d = b; 
d += c/320f; 
// d = 50.484375 

lo haría mucho prefiero esto:

int a = 50155; 
int b = 10210; 
int c = MyClass.Add(a.b); // c = 61045 
... 
public int Add(int a, int b) 
{ 
    // ????? 
} 

Gracias de antemano por la ayuda!

+2

Huele a tarea. – Oded

+0

¿Puede mostrarnos lo que ha intentado hasta ahora? –

+2

Bio muestra la industria comercial , y cara a cara que trabajar en 32 de un dólar tiene que ser un poco extraño, incluso para la tarea :) – Andrew

Respuesta

1

Parece una codificación extraña para mí.

De todos modos, si el formato es de 10 bases Nxxx donde N es un número entero que denota dólares enteros y xxx se interpreta como

(xxx/320)

y desea sumarlos, la lo único que necesita para manejar es hacer acarreo cuando xxx excede 320:

int a = ..., b = ...; // dollar amounts 
int c = (a + b); // add together 
// Calculate carry 
int carry = (c % 1000)/320; // integer division 
c += carry * 1000; 
c -= carry * 320; 
// done 

Nota: esto funciona porque si a y B están codificados correctamente, las partes fraccionarias se suman a 638 como máximo y por lo tanto no hay " desbordamiento "a la parte de dólares enteros.

4

Bueno, yo no creo que es necesario utilizar coma flotante ...

public static int Add(int a, int b) 
{ 
    int firstWhole = a/1000; 
    int secondWhole = b/1000; 
    int firstFraction = a % 1000; 
    int secondFraction = b % 1000; 
    int totalFraction = firstFraction + secondFraction; 
    int totalWhole = firstWhole + secondWhole + (totalFraction/320); 
    return totalWhole * 1000 + (totalFraction % 320); 
} 

Alternativamente, es posible que desee crear una estructura personalizada que se puede convertir ay desde el formato de número entero, y sobrecarga el + operador. Eso le permitiría escribir un código más legible que no condujo accidentalmente a otros enteros siendo tratados como este formato un tanto extraño.

EDIT: Si uno se ve obligado a seguir con un formato de "solo entero", pero llegar a ajustarlo tanto es posible que desee considerar el uso de 512 en lugar de 1000. De este modo puede usar máscara simple y desplazamiento:

public static int Add(int a, int b) 
{ 
    int firstWhole = a >> 9; 
    int secondWhole = b >> 9; 
    int firstFraction = a & 0x1ff 
    int secondFraction = b & 0x1ff; 
    int totalFraction = firstFraction + secondFraction; 
    int totalWhole = firstWhole + secondWhole + (totalFraction/320); 
    return (totalWhole << 9) + (totalFraction % 320); 
} 

Todavía hay problemas con 320, pero al menos es algo mejor.

+0

Gracias amigo, buena respuesta, pero según mis pruebas, mi solución es dos veces más rápida. Demasiado modulo aritmático, creo. ¿Alguna otra idea? –

+0

Si desea evitar la división, puede usar un desplazamiento a la derecha en modo bit. –

+0

@Steve H: ¿Qué solución? Solo has mostrado una implementación parcial usando flotadores. Podrías usar Math.DivRem que * podría * hacerlo más rápido ... pero ¿es esto realmente un cuello de botella en primer lugar? Tenga en cuenta que si puede usar una estructura personalizada, es posible que pueda hacer muchas de las operaciones sin hacer la multiplicación y el resto con la misma frecuencia. –

3

Rompe la cuerda en la parte que representa dólares enteros, y la parte que representa fracciones de dólares. Para este último, en lugar de tratarlo como 10.5 treinta segundos de un dólar, probablemente sea más fácil tratarlo como 105 trescientos veinte de un dólar (es decir, multiplicar ambos por diez para el numerador es siempre un número entero).

A partir de ahí, hacer matemáticas es bastante simple (aunque algo tedioso de escribir): sume las fracciones. Si eso excede un dólar entero, lleve un dólar (y reste 320 de la parte de la fracción). Luego agrega todo el dinero. Resta también, aunque en este caso debes tener en cuenta los préstamos en lugar de llevarlos.

0

Si insiste en trabajar en ints, no puede resolver su problema sin analizarlo, después de que todos sus datos no sean enteros. Pongo en evidencia las (hasta ahora) 3 respuestas que analizan todos tus datos en sus componentes antes de realizar la aritmética.

Una alternativa sería usar números racionales con 2 componentes (enteros), uno para la parte completa y uno para el número de 320ths en la parte fraccionaria. Luego implementa la aritmética racional apropiada. Como siempre, elija cuidadosamente sus representaciones de datos y sus algoritmos serán mucho más fáciles de implementar.

No puedo decir que creo que esta alternativa sea particularmente mejor en cualquier eje de comparación, pero podría satisfacer su deseo de no analizar.

+0

También puede venir en cadena. –

2

Como punto de aprendizaje, esta representación se llama "fixed point". Hay una serie de implementaciones que puedes mirar. Le sugiero encarecidamente que NO utilice int como su tipo de datos de nivel superior, sino que cree un tipo llamado Fixed que encapsule las operaciones. Mantendrá la cuenta regresiva de errores cuando erróneamente agregue un int plano a un número de punto fijo sin escalar primero, o escale un número y olvide escalarlo.

+0

Aprendiendo algo nuevo todos los días. Gracias plinto. :) –

3

Editar:
Esta respuesta sugiere que uno "se mantiene lejos" de la aritmética flotante. Sorprendentemente, el OP indicó que su lógica basada en flotador (no mostrada por razones de propiedad) era dos veces más rápida que la solución de módulo entero a continuación. Viene a mostrar que las FPU no son tan malas después de todo ...

Definitivamente, manténgase alejado de los flotadores (para este problema en particular). La aritmética de enteros es más eficiente y no presenta problemas de redondeo de errores.

Algo como lo siguiente debería hacer el truco
Nota: Como está escrito, supone que A y B son positivos.

int AddMyOddlyEncodedDollars (int A, int B) { 
    int sum; 
    sum = A + B 
    if (sum % 1000 < 320); 
    return sum 
    else 
    return sum + 1000 - 320; 
} 

Editar: En la eficiencia del operador módulo en C
I depende en gran medida del compilador ... Puesto que el valor del módulo es conocido en tiempo de compilación, yo esperaría compiladores más modernos a vaya al enfoque "multiplicar [por recíproco] y cambiar", y esto es rápido.
Esta preocupación por el rendimiento (con este formato bastante artificial) es una llamada a la optimización prematura, pero una vez más, he visto el software en la industria financiera muy optimizado (por decirlo cortésmente), y con razón.

+0

Oooo Me gusta esto ... Fuera de mi cabeza No sé la velocidad aritmética del módulo - ¿tú? –

+0

Lo sentimos, me encanta esta solución y funciona muy bien, pero modulo, en mis pruebas para mi propósito, es dos veces más lento que mi solución. :(Gracias. –

+0

@Steve H. He leído los comentarios en la pregunta, me alegro de que confirme mi intuición de que podría ser para el mundo financiero. Editaré mi respuesta para indicar que mi cansancio sobre la aritmética flotante fue erróneamente colocado. , Las FPU son increíblemente rápidas, y obviamente todos debemos aprender de los hackers financieros ... – mjv

0

TEN CUIDADO: Este post está mal, mal , mal. Lo eliminaré tan pronto como deje de sentirme tonto por intentarlo.

Aquí está mi ir: Puede espacio comercial por tiempo.

Construya un mapeo para los primeros 10 bits de una tupla: recuento de dólares, número de piezas de32. Luego use la manipulación de bits en su entero:

  • ignore los bits 11 y superiores, aplique el mapa.
  • cambio de toda la serie 10 veces, añadir pequeñas dólares al cambio de cartografía por encima de
  • ahora tiene la amoung dólar y la cantidad piecesof32
  • añadir tanto
    • movimiento desbordamiento de cantidad en dólares

A continuación, para volver a la notación "canónica", necesita un mapa de búsqueda inversa para sus piezas de 32 y "pedir prestado" dólares para llenar los bits. Desarme los dólares 10 veces y agregue las piezas de 32.

EDIT: Debo eliminar esto, pero estoy muy avergonzado. Por supuesto, no puede trabajo. Soy tan estúpido :(

La razón es que desplazar por 10 a la derecha es lo mismo que dividir por 1024 - no es como si algunos de los bits más bajos tuvieran una cantidad en dólares y algunas partes en una cantidad de 32. Decimal y la notación binaria simplemente no se divide muy bien. Por eso usamos la notación hexadecimal (agrupación de 4 bits). Bummer.

+0

Hm, idea interesante. Ejemplo de ello ¿Ay? –

+0

Estoy trabajando en un ejemplo, y en su defecto. Aprendiendo mucho acerca de los números a medida que voy :) –