1

I have 32 GB RAM. The variable ram_in_gb returns 31.917182922363281. I want to round it to 31.9. I expect the buffer to return 31.9 GB string. There was an easy way to do that using wvprintf, sprintf or something like that. I don't remember how I used to do it in the past, so here is my question.

// Usage
wchar_t ram[64]{};
get_ram(ram);

bool get_ram(LPWSTR buffer)
{
    auto memory_status{ MEMORYSTATUSEX {} };
    memory_status.dwLength = sizeof MEMORYSTATUSEX;
    const auto result = GlobalMemoryStatusEx(&memory_status);

    if (result)
    {
        auto ram_in_gb = static_cast<double>(memory_status.ullTotalPhys) / (1024 * 1024 * 1024);
        
        wcscpy(buffer, );
    }
        
    return result;
}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
nop
  • 4,711
  • 6
  • 32
  • 93
  • Would something like `ram_in_gb = std::round(ram_in_gb * 10.0) / 10.0;` be precise enough for you? –  Jun 25 '21 at 14:06
  • @Frank, preferrably I want to avoid anything from `std::` for now. – nop Jun 25 '21 at 14:08
  • 1
    `int(ram_in_gb * 10.0 + .5)` should be equivalent to `std::round(ram_in_gb * 10.0)` – Igor Tandetnik Jun 25 '21 at 14:10
  • @IgorTandetnik, I'm receiving `319` like that. But I want " GB" suffix as well. It's not only the rounding. That's why I mentioned wvprinf. I don't remember which API call exactly it was, but it was easier that way "%.2 GB" or something like that. – nop Jun 25 '21 at 14:13
  • The `/ 10.0` part was left as an exercise for the reader. – Igor Tandetnik Jun 25 '21 at 14:14
  • 1
    1024 * 1024 * 1024 isn't portable. – Bathsheba Jun 25 '21 at 14:21
  • @IgorTandetnik: Fails on at least one edge case in IEEE754, but most people are pretty chilled about that. – Bathsheba Jun 25 '21 at 14:23
  • You are probably looking for [`swprintf`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/sprintf-sprintf-l-swprintf-swprintf-l-swprintf-l) – Igor Tandetnik Jun 25 '21 at 14:26
  • @Bathsheba Wouldn't the parens cause the `1024*1024*1024` to be done as `int` operations and only the result be used as a `double`? Is that still technically non-portable? (genuinely curious here). –  Jun 25 '21 at 14:26
  • INT_MAX can be as small as 32767. On such a platform, 1073741824 would be a `long` literal. `1024*1024*1024` is always a compile time evaluable constant expression of type `int`. At the time of my writing I think more "things" running C++ and C code have a 16 bit `int` than any other size. – Bathsheba Jun 25 '21 at 14:32
  • @Bathsheba Yeah, that much is obvious. I guess I interpreted you referencing IEEE754 as implying that there was something about the floating point behavior of the expression being non-portable. Thanks for the clarification –  Jun 25 '21 at 14:53
  • A function API taking a writable buffer pointer and not its size is susceptible to buffer overflow. If you had the size you could use `swprintf_s`. – rustyx Jun 25 '21 at 15:10
  • @Frank: My IEEE754 thing was on the (int)(x + 0.5) standing in for round(x). It's not always true. See my lovely (?!) answer https://stackoverflow.com/questions/47302544/why-do-lots-of-old-programs-use-floor0-5-input-instead-of-roundinput/47302585#47302585 – Bathsheba Jun 25 '21 at 15:58
  • @IgorTandetnik, I think so – nop Jun 25 '21 at 16:49
  • @AdrianMole, but it's not MFC. – nop Jun 25 '21 at 19:06
  • @AdrianMole, `ram = 0x008ff628 L"f GB"`. Something is wrong. https://pastebin.com/Rb1Hu7Pk – nop Jun 25 '21 at 19:14
  • @AdrianMole, that's what I wanted! Thank you! Even tho it says it's deprecated and it would be good to be replaced with something else – nop Jun 25 '21 at 21:06

1 Answers1

1

Although it's 'old' and very "C-Style", you can use the std::swprintf function for this, using the precision field of the %lf format specifier set to 1 (or how ever many decimal places you want to show). You should pass the buffer length as a parameter to your get_ram() function, as the swprintf call needs that:

#include <cwchar> // For swprintf declaration

bool get_ram(LPWSTR buffer, size_t buflen)
{
    auto memory_status{ MEMORYSTATUSEX {} };
    memory_status.dwLength = sizeof MEMORYSTATUSEX;
    const auto result = GlobalMemoryStatusEx(&memory_status);
    if (result)
    {
        auto ram_in_gb = static_cast<double>(memory_status.ullTotalPhys) / (1024.0 * 1024.0 * 1024.0);      
        swprintf(buffer, buflen, L"%.1lf GB", ram_in_gb); // Use of "1024.0" above avoids integer overflow ^
    }
    return result;
}

From the "Format Specifiers" table (the entry for the f format) in the above cppreference link:

Precision specifies the exact number of digits to appear after the decimal point character. The default precision is 6. In the alternative implementation decimal point character is written even if no digits follow it. For infinity and not-a-number conversion style see notes.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83