Я думаю, что работает следующее, но сначала я выскажу свои предположения:
- числа с плавающей точкой хранятся в формате IEEE-754 в вашей реализации,
- без переполнения,
- У вас есть
nextafterf()
доступно (это указано в C99).
Также, скорее всего, этот метод не очень эффективен.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(int argc, char *argv[])
{
/* Change to non-zero for superior, otherwise inferior */
int superior = 0;
/* double value to convert */
double d = 0.1;
float f;
double tmp = d;
if (argc > 1)
d = strtod(argv[1], NULL);
/* First, get an approximation of the double value */
f = d;
/* Now, convert that back to double */
tmp = f;
/* Print the numbers. %a is C99 */
printf("Double: %.20f (%a)\n", d, d);
printf("Float: %.20f (%a)\n", f, f);
printf("tmp: %.20f (%a)\n", tmp, tmp);
if (superior) {
/* If we wanted superior, and got a smaller value,
get the next value */
if (tmp < d)
f = nextafterf(f, INFINITY);
} else {
if (tmp > d)
f = nextafterf(f, -INFINITY);
}
printf("converted: %.20f (%a)\n", f, f);
return 0;
}
На моей машине он печатает:
Double: 0.10000000000000000555 (0x1.999999999999ap-4)
Float: 0.10000000149011611938 (0x1.99999ap-4)
tmp: 0.10000000149011611938 (0x1.99999ap-4)
converted: 0.09999999403953552246 (0x1.999998p-4)
Идея состоит в том, что я преобразовываю значение double
в значение float
- это может быть меньше или больше двойного значения в зависимости от режима округления. При преобразовании обратно в double
мы можем проверить, меньше ли оно или больше исходного значения. Затем, если значение float
не в правильном направлении, мы смотрим на следующее float
число из преобразованного числа в направлении исходного числа.