Получение размера ОЗУ в C, linux, неточном результате

У меня 16 ГБ оперативной памяти на компьютере в школе под управлением Ubuntu 13.10. Я написал простую программу на C, чтобы получить полную ОЗУ, но она показывает неточные результаты, вместо 16 выдает 15. Как сделать это точным даже для меньших размеров ОЗУ? Дома у меня есть компьютер с ОЗУ 768 МБ, и моя программа также показывает неправильные результаты :(

#include <sys sysctl.h="">
#include <sys types.h="">
#include <stdlib.h>
#include <stdio.h>

void *************()
{
 FILE *meminfo = fopen("/proc/meminfo", "r");
 int totalMemory = 0;
 if(meminfo == NULL)
 {
 exit(-1);
 }
 char buff[256];
 while(fgets(buff, sizeof(buff), meminfo))
 {
 int ramKB;
 if(sscanf(buff, "MemTotal: %d kB", &ramKB) == 1)
 {
 totalMemory = ramKB/1024.0;
 totalMemory = totalMemory/1024.0;
 }
 }
 if(fclose(meminfo) != 0)
 {
 exit(-1);
 }
 printf("%d\n", totalMemory);
}

int main()
{
 *************();

 return 0;
}
</stdio.h></stdlib.h></sys></sys>
2 ответа

То, что вы видите, вызвано неточностями с плавающей запятой. Вы должны быть осторожны при переходе между плавающими точками и целыми числами и при печати плавающих точек.

Поскольку плавающие точки не могут точно представлять все числа, он часто пытается приблизиться как можно ближе. Скорее всего, это то, что вместо 16, totalMemory - это что-то вроде 15.999999999987 а затем, когда он преобразуется в int, он обрезается до 15.

Есть два способа исправить это: если вы знаете, что totalMemory делится на 1024*1024, тогда просто используйте целые числа (это не работает в случае не целочисленных гигабайт). Так как вы все равно используете целое число, вы можете использовать этот подход. (768 МБ не может быть выражено как интегральное количество ГБ).

Другой вариант - добавить epsilon, чтобы предотвратить это. Другими словами, вместо использования totalMemory вы использовали бы что-то вроде totalMemory + 1e-7. Эпсилон слишком ничтожен, чтобы сделать значимую разницу, но он может подтолкнуть что-то вроде 15.999... до 16.

Кстати, проблемы с плавающей запятой являются лишь частью вашей проблемы. Если вы собираетесь использовать целое число, вы, вероятно, захотите использовать MB вместо GB. Как целое число может представлять собой что-то вроде 4.5 ГБ (хотя в наши дни это очень редко)?


Я когда-то использовал этот код. , , Работает для большинства ОС

#if defined(_WIN32)
#include <windows.h>

#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#include <sys types.h="">
#include <sys param.h="">
#if defined(BSD)
#include <sys sysctl.h="">
#endif

#else
#error "Unable to define getMemorySize( ) for an unknown OS."
#endif

/**
 * Returns the size of physical memory (RAM) in bytes.
 */
size_t getMemorySize( )
{
#if defined(_WIN32) && (defined(__CYGWIN__) || defined(__CYGWIN32__))
 /* Cygwin under Windows. ------------------------------------ */
 /* New 64-bit MEMORYSTATUSEX isn't available. Use old 32.bit */
 MEMORYSTATUS status;
 status.dwLength = sizeof(status);
 GlobalMemoryStatus( &status );
 return (size_t)status.dwTotalPhys;

#elif defined(_WIN32)
 /* Windows. ------------------------------------------------- */
 /* Use new 64-bit MEMORYSTATUSEX, not old 32-bit MEMORYSTATUS */
 MEMORYSTATUSEX status;
 status.dwLength = sizeof(status);
 GlobalMemoryStatusEx( &status );
 return (size_t)status.ullTotalPhys;

#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
 /* UNIX variants. ------------------------------------------- */
 /* Prefer sysctl() over sysconf() except sysctl() HW_REALMEM and HW_PHYSMEM */

#if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64))
 int mib[2];
 mib[0] = CTL_HW;
#if defined(HW_MEMSIZE)
 mib[1] = HW_MEMSIZE; /* OSX. --------------------- */
#elif defined(HW_PHYSMEM64)
 mib[1] = HW_PHYSMEM64; /* NetBSD, OpenBSD. --------- */
#endif
 int64_t size = 0; /* 64-bit */
 size_t len = sizeof( size );
 if ( sysctl( mib, 2, &size, &len, NULL, 0 ) == 0 )
 return (size_t)size;
 return 0L; /* Failed? */

#elif defined(_SC_AIX_REALMEM)
 /* AIX. ----------------------------------------------------- */
 return (size_t)sysconf( _SC_AIX_REALMEM ) * (size_t)1024L;

#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
 /* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */
 return (size_t)sysconf( _SC_PHYS_PAGES ) *
 (size_t)sysconf( _SC_PAGESIZE );

#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGE_SIZE)
 /* Legacy. -------------------------------------------------- */
 return (size_t)sysconf( _SC_PHYS_PAGES ) *
 (size_t)sysconf( _SC_PAGE_SIZE );

#elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM))
 /* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */
 int mib[2];
 mib[0] = CTL_HW;
#if defined(HW_REALMEM)
 mib[1] = HW_REALMEM; /* FreeBSD. ----------------- */
#elif defined(HW_PYSMEM)
 mib[1] = HW_PHYSMEM; /* Others. ------------------ */
#endif
 unsigned int size = 0; /* 32-bit */
 size_t len = sizeof( size );
 if ( sysctl( mib, 2, &size, &len, NULL, 0 ) == 0 )
 return (size_t)size;
 return 0L; /* Failed? */
#endif /* sysctl and sysconf variants */

#else
 return 0L; /* Unknown OS. */
#endif
}
</sys></sys></sys></unistd.h></windows.h>

licensed under cc by-sa 3.0 with attribution.