Здесь есть две отдельные проблемы. Во-первых, достаточно ли точности для двойного числа, чтобы хранить все нужные вам биты, а во-вторых, где он может точно представлять ваши числа.
Что касается точного представления, вы правы быть осторожными, потому что точная десятичная дробь, такая как 1/10, не имеет точного двоичного аналога. Однако, если вы знаете, что вам нужно только 5 десятичных цифр точности, вы можете использовать масштабированная арифметика, в которой вы оперируете числами, умноженными на 10 ^ 5. Например, если вы хотите точно представить 23.7205, вы должны представить его как 2372050.
Давайте посмотрим, достаточно ли точности: двойная точность дает вам 53 бита точности.
Это эквивалентно 15+ десятичным цифрам точности. Так что это позволит вам пять цифр после десятичной точки и 10 цифр перед десятичной точкой, что кажется достаточным для вашего приложения.
Я бы поместил этот код C в файл .h:
typedef double scaled_int;
#define SCALE_FACTOR 1.0e5 /* number of digits needed after decimal point */
static inline scaled_int adds(scaled_int x, scaled_int y) { return x + y; }
static inline scaled_int muls(scaled_int x, scaled_int y) { return x * y / SCALE_FACTOR; }
static inline scaled_int scaled_of_int(int x) { return (scaled_int) x * SCALE_FACTOR; }
static inline int intpart_of_scaled(scaled_int x) { return floor(x / SCALE_FACTOR); }
static inline int fraction_of_scaled(scaled_int x) { return x - SCALE_FACTOR * intpart_of_scaled(x); }
void fprint_scaled(FILE *out, scaled_int x) {
fprintf(out, "%d.%05d", intpart_of_scaled(x), fraction_of_scaled(x));
}
Вероятно, есть несколько грубых пятен, но этого должно быть достаточно, чтобы начать работу.
Никаких накладных расходов на добавление, стоимость умножения или деления удваивается.
Если у вас есть доступ к C99, вы также можете попробовать масштабированную целочисленную арифметику, используя int64_t
64-битный целочисленный тип. Что будет быстрее, зависит от вашей аппаратной платформы.