How to multiply by Decimal Quantities without using Floating Point? [C17]

79 views Asked by At

I'm trying to code a sort of ATM machine in C. I've been Strongly advised to not use floating points at any time when dealing with currency.
In my code the user can select which currency they want to work with ($,E (€),P (pound)).
The problem arrises when trying to make an exchange from one currency to another one, as they cannot be represented by an int variable.

The Exchange Rates Chosen:
US to EU is 0.91
US to UK is 0.79
Note that these aren't constants, the user can change them.

Code fragment:

#include <stdio.h>
#include <math.h>
#include <string.h>

struct Currency {
    int main;
    int frac;
    char symb[1];
} Dinero, Banka;

struct Currency Dinero = {
    0, 0, "E" //E is €
};

struct Currency Banka = {
    0, 0, "$"
};

int UStoEU_ExchangeRate = 91 / 100, UStoUK_ExchangeRate = 79 / 100; //Exchange rates
int EU_US_ExchangeRate = 110 / 100, UK_US_ExchangeRate = 127 / 100;

int CalcCuentmain() { //Function that takes the value from "Banka"(always in $) and translates them into the different currencies the user asked.

    int RVmain; //Is used in int main() to print the RealValue times the exchangerate.
    if (strcmp(Dinero.symb, "$") == 0) {
        RVmain = Banka.main;
        return RVmain;
    }
    if (strcmp(Dinero. symb, "E") == 0) {
        RVmain = US_EU_ExchangeRate * Banka.main; //Doesn't Work
        return RVmain;
    }
    if (strcmp(Dinero.symb, "P") == 0) {
        RVmain = US_UK_ExchangeRate * Banka.main; //Doesn't Work
        return RVmain;
    }
}

int main() { ... }

From the code, line:

    RVmain = US_EU_ExchangeRate * Banka.main; //Doesn't Work

This won't change the value of RVmain as the US_EU_ExchangeRate is rounded to 1.

Is there any way to multiply RVmain by a decimal quantity (in this case 0.91) without using floating points?
If not how do we deal with problems like this when working with currencies?

Important Notes:

  • I'm very new to programming so apologies in advance if this is quite a simple problem or badly phrased.

  • I represent currency using a structure. Using Dinero when there is a user input, and Banka to store "the real value". int main is the number before the decimal dot, int frac is the number after the decimal dot.

        struct Currency {
            int main;
            int frac;
            char symb[1];
        } Dinero, Banka;
    
  • I know that even this fragment alone might(is) poorly written, but please remember I'm a beginner :(

  • If there is some ambiguity in my question I'll happy clarify it.

1

There are 1 answers

0
chux - Reinstate Monica On

Is there any way to multiply RVmain by a decimal quantity (in this case 0.91) without using floating points?

Scale by 91, then divide by 100.

  1. Use a wide enough integer math to avoid overflow.

  2. Consider how you want to round the result. Round to nearest, half-way away for example. Simply add half the denominator before the division. Also consider negative dollar amounts.

  3. Assumption: conversion rate is never negative.

Example:

int US_EU_ExchangeRate_numerator = 91;
int US_EU_ExchangeRate_denominator = 100;
long long t = (long long)US_EU_ExchangeRate_numerator * Banka.main;
if (Banka.main >= 0) { 
  RVmain = (int) ((t + US_EU_ExchangeRate_denominator/2)/US_EU_ExchangeRate_denominator);
} else {
  RVmain = (int) ((t - US_EU_ExchangeRate_denominator/2)/US_EU_ExchangeRate_denominator);
}

To be more like floating point common math, more advanced code would use Round to nearest, half-way to even. Something like:

long long t = (long long)US_EU_ExchangeRate_numerator * Banka.main;
int RVmain = (int) (t / US_EU_ExchangeRate_denominator);
int remainder = t % US_EU_ExchangeRate_denominator;
if (Banka.main >= 0) { 
  RVmain += +2*remainder > US_EU_ExchangeRate_denominator || 
      (RVmain % 2 && 2*remainder == US_EU_ExchangeRate_denominator);
} else {
  RVmain -= -2*remainder > US_EU_ExchangeRate_denominator || 
      (RVmain % 2 && -2*remainder == US_EU_ExchangeRate_denominator);
}

Create a helper function to handle conversions uniformly.

RVmain = money_convert(Banka.main, US_EU_ExchangeRate_numerator, US_EU_ExchangeRate_denominator);