-1

I need to multiply two values ​​- weight and currency (Visual c++, mfc). E.g.:

a=11.121;
b=12.11;
c=a*b;

Next I have to round "с" to 2 digits after point (currency value, e.g. 134.68). What the best data types and rounding function for this variables? The rounding procedure must be mathematically correct.

P.S. The problem was solved by very ugly but working part of code:

CString GetPriceSum(CString weight,CString price)
{
    price.Replace(".", "");
    price = price + "0";
    if (weight.Find(".") == -1) { weight = weight + ".000"; }
    weight.Replace(".", "");
    unsigned long long int iprice = atoi(price);
    unsigned long long int iweight = atoi(weight);
    unsigned long long int isum = iprice * iweight;
    CString sum = ""; sum.Format("%llu", isum);
    CString r1 = sum.Right(1);
    if (atoi(r1) >= 5) { isum += 10; }

    CString r2 = sum.Mid(sum.GetLength() - 2, 1);
    if (atoi(r2) >= 5) { isum += 100; sum.Format("%llu", isum);}

    r2 = sum.Mid(sum.GetLength() - 3, 1);
    if (atoi(r2) >= 5) { isum += 1000; sum.Format("%llu", isum);}

    r2 = sum.Mid(sum.GetLength() - 4, 1);
    if (atoi(r2) >= 5) { isum += 10000; sum.Format("%llu", isum);}

    CString finsum = ""; finsum.Format("%llu", isum);
    finsum.Insert(finsum.GetLength() - 6, ".");
    finsum.Delete(finsum.GetLength() - 4, 4);
    if (finsum.Left(1) == ".") { finsum = "0" + finsum; }
    return finsum;
}
  • 2
    Does this answer your question? [Best way to store currency values in C++](https://stackoverflow.com/questions/149033/best-way-to-store-currency-values-in-c) – IInspectable Jan 20 '22 at 20:44
  • I do not need and extra library or some extra data type. I need some advice, to multiply two values, one with 2 digits after point, and other with 3 digits. And then, mathematically round the result to value with 2 digits after point. P.S. Dear IInspectable, please, do not comment, if you have nothing useful to advice. – Сергей Соколов Jan 20 '22 at 20:54
  • Binary values are incompatible with your concept of `"2 digits after point"`; is your question about how to print two digits after the decimal point? – Vlad Feinstein Jan 20 '22 at 21:28
  • 1
    I will argue that you **do** need a library, you just don't know yet. Binary floating point values cannot be used to accurately store decimals, as I commented on your [previous question](https://stackoverflow.com/q/70685592/1889329) already. Anyway, up to you to decide what's more important: Solving the problem or avoiding a library dependency. – IInspectable Jan 20 '22 at 21:32
  • Side note: I think that the coment by @IInspectable is pretty useful. Did you read it? It offers multiple (and contradictory) opinions on what data type to use, without additional libraries. – Vlad Feinstein Jan 20 '22 at 21:32
  • I need to: Multiply values. Round values to 2 digits after point (mathematically correct, important!). Print value. – Сергей Соколов Jan 20 '22 at 21:33
  • May be I can not believe that there is no simple way for this procedure. – Сергей Соколов Jan 20 '22 at 21:35
  • Reminded me: "Go into a dark cellar with an extinguished candle to find a black cat that is not there." :) – Vlad Feinstein Jan 20 '22 at 21:41
  • 2
    That's not what the question is asking for. If the problem you are trying to solve is printing a floating point value to a given precision, then the `printf` family of functions can do that, including the rounding. There will be surprises, since binary floating point values cannot accurately represent decimal numbers (e.g. `0.1`). – IInspectable Jan 20 '22 at 21:41
  • IInspectable -That is the point! API I use, counts values using some other language. And they round they values mathematically correct. I need to count my values to be equal, and convert them to strings. – Сергей Соколов Jan 20 '22 at 21:44
  • So the question persists. What is the SIMPLIEST way to solve this problem? Two values, 2 and 3 digits after point. Multiply them, and get the rounded value, with 2 digits after point. Convert the last value to string. – Сергей Соколов Jan 20 '22 at 21:54
  • What is that rounded value supposed to be if it cannot be represented (like `0.1`)? It seems you are still struggling to understand that the concept of **decimals** does not exist for binary floating point values. – IInspectable Jan 20 '22 at 22:01
  • OMG, I did not want to struggle with concepts. I need a simplest way to multiply currency with weight, round the result, and convert to string. Nothing else. – Сергей Соколов Jan 20 '22 at 22:07
  • Then you need a computer that operates on decimals, not binary numbers. Or use a library. But you didn't want that... – Vlad Feinstein Jan 20 '22 at 22:09
  • You **cannot** round a binary floating point value to a precision given in decimals. That's a fact, and a reality you have to come to terms with. If you want to **print** a binary floating point value up to a given number of decimals, then that **can** be done using the `printf` family of functions. But apparently you want to first round, then print. – IInspectable Jan 20 '22 at 22:10

1 Answers1

1

How about this: let's start from

API I use, counts values using some other language. And they round they values mathematically correct.

In your other question, you got those value as strings. You can construct an integer from those digits (remove decimal point). Assuming that the product fits in a 64-bit int, you can multiply them exactly. Now you can manually round to a desired precision and drop unneeded digits.

Code example (you may want to add error checking):

#define _CRT_SECURE_NO_WARNINGS

#include <string>
#include <iostream>
#include <sstream>

int main()
{
    std::string a = "40.50";
    std::string b = "0.490";

    long long l1, dec1, l2, dec2;

    sscanf(a.data(), "%lld.%lld", &l1, &dec1);
    l1 = l1 * 100 + dec1;
    sscanf(b.data(), "%lld.%lld", &l2, &dec2);
    l2 = l2 * 1000 + dec2;

    long long r = l1 * l2;
    r /= 100;
    int rem = r % 10;
    r /= 10;
    if (rem >= 5)
        r++;

    std::stringstream ss;
    ss << r / 100 << "." << std::setw(2) << std::setfill('0') << r % 100;
    std::cout << ss.str();
}

You can also use stringstream instead of sscanf to parse the strings.

Vlad Feinstein
  • 10,960
  • 1
  • 12
  • 27
  • Ok, can you help me with the code? Because my attempts did not helps me :( – Сергей Соколов Jan 20 '22 at 22:10
  • @СергейСоколов sure, a bit later (still a work day here) – Vlad Feinstein Jan 20 '22 at 22:11
  • Thank you in advance. It was a simple question - but I can not solve it for 2 weeks :( – Сергей Соколов Jan 20 '22 at 22:13
  • The first, mandatory step is understanding what this undisclosed API really does. Since it cannot round (as explained above), it must be doing something else. If we don't understand what that something else is, there's no way to produce an answer. – IInspectable Jan 20 '22 at 22:14
  • Api rounding is mathematically correct. I have to do the same. That is all. – Сергей Соколов Jan 20 '22 at 22:18
  • That's wrong. The first thing you have to understand is, that floating point numbers **do not behave like rational numbers**. As a simple example, looking at all values with 1 decimal between 1 and 2, only `1.0` and `1.5` can be represented as finite binary floating point values. `1.1`, `1.2`, `1.3`, `1.4`, `1.6`, `1.7`, `1.8`, and `1.9` have no finite binary floating point representation. Once you understand that you can start to investigate what this API really does. When that is done you can attempt to replicate that behavior. – IInspectable Jan 20 '22 at 22:25
  • @СергейСоколов please see my updated answer – Vlad Feinstein Jan 21 '22 at 00:53
  • This code returns different results sometimes. Sometim it is 45.23 - all is ok, sometime - 41.2 (only one digit after point), and sometime 45.560 (3 digits after point). Sometime the result is wrong: 215.00*0.428 returns 92.2, but must return 92.02. – Сергей Соколов Jan 29 '22 at 07:34
  • @СергейСоколов Oops… the leading 0 will be dropped for the decimal part. Can be fixed with setw(2). Don’t see how 3 decimals can get printed – Vlad Feinstein Jan 29 '22 at 15:32
  • 1
    @СергейСоколов I found an error (copy/paste kind, constructed `ss` from `a`). Fixed. Also fixed leading 0. Should be OK now. – Vlad Feinstein Jan 29 '22 at 21:38
  • It is marvelous but after tests (aprox. 10 000 different operations) the ugly code in question still working better. E.G. 158.473*6.5=1030.08 in ugly code and 1030.07 in code in comment. – Сергей Соколов Jan 31 '22 at 17:34
  • @СергейСоколов what is your point? The calculator says that `158.473*6.5` is`1030.0745`, so `1030.07` looks correct. – Vlad Feinstein Jan 31 '22 at 17:44
  • Rounding to nearest integer (WIKI) Rounding to the nearest integer is the most commonly used rounding, in which a number is rounded to an integer, the modulus of the difference with which this number has a minimum. if N+1 character < 5, then the Nth character is retained, and N+1 and all subsequent characters are set to zero; if N+1 digit ≥ 5, then the Nth digit is increased by one, and N+1 and all subsequent ones are set to zero; – Сергей Соколов Jan 31 '22 at 18:07
  • @СергейСоколов sounds like you are arguing MY point! :) `4` in `.0745` is less than `5`, so `7` remains - right? – Vlad Feinstein Jan 31 '22 at 18:39
  • 158.473*6.5=1 030,0745 = 1 030,075 = 1 030,08. We start round from the last digit – Сергей Соколов Jan 31 '22 at 18:56
  • @СергейСоколов this is NOT how rounding works! – Vlad Feinstein Jan 31 '22 at 19:05
  • This is how rounding works in api I need to send and compare my data. And there are several different ways of rounding - and as I understand, this is one of them. – Сергей Соколов Jan 31 '22 at 19:33