"Почти делимый"
Я хочу проверить, является ли значение с плавающей запятой "почти" кратным 32. Например. 64.1 "почти" делится на 32, и 63,9.
Сейчас я делаю это:
#define NEARLY_DIVISIBLE 0.1f
float offset = fmodf( val, 32.0f ) ;
if( offset < NEARLY_DIVISIBLE )
{
// its near from above
}
// if it was 63.9, then the remainder would be large, so add some then and check again
else if( fmodf( val + 2*NEARLY_DIVISIBLE, 32.0f ) < NEARLY_DIVISIBLE )
{
// its near from below
}
Есть ли лучший способ сделать это?
Ответы
Ответ 1
ну, вы можете вырезать второй fmodf, просто вычитая 32 еще раз, чтобы получить мода снизу.
if( offset < NEARLY_DIVISIBLE )
{
// it near from above
}
else if( offset-32.0f>-1*NEARLY_DIVISIBLE)
{
// it near from below
}
Ответ 2
В стандартно-совместимой реализации C вместо fmod
следует использовать функцию remainder
:
#define NEARLY_DIVISIBLE 0.1f
float offset = remainderf(val, 32.0f);
if (fabsf(offset) < NEARLY_DIVISIBLE) {
// Stuff
}
Если вы находитесь на несовместимой платформе (например, MSVС++), то remainder
недоступно, к сожалению. Я думаю, что ответ быстрого умножения в этом случае вполне резонный.
Ответ 3
Вы упомянули, что вам нужно проверить почти делимость с 32. Следующая теория должна быть справедлива для тестирования почти делимости против степеней двух:
#define THRESHOLD 0.11
int nearly_divisible(float f) {
// printf(" %f\n", (a - (float)((long) a)));
register long l1, l2;
l1 = (long) (f + THRESHOLD);
l2 = (long) f;
return !(l1 & 31) && (l2 & 31 ? 1 : f - (float) l2 <= THRESHOLD);
}
То, что мы делаем, это принудительное поплавок, а float + THRESHOLD - длительное время.
f (long) f (long) (f + THRESHOLD)
63.9 63 64
64 64 64
64.1 64 64
Теперь мы тестируем, если (long) f делится на 32. Просто проверьте младшие пять бит, если все они установлены на ноль, число делится на 32. Это приводит к ряду ложных срабатываний: от 64.2 до 64.8, при преобразовании в длинные, также равны 64 и будут проходить первый тест. Итак, мы проверяем, меньше ли различие между их укороченной формой и f, чем THRESHOLD.
У этого тоже есть проблема: f - (float) l2 <= THRESHOLD будет иметь значение для 64 и 64.1, но не для 63.9. Таким образом, мы добавляем исключение для чисел, меньших 64 (которые при добавлении THRESHOLD и впоследствии принудительно длинные - отмечают, что обсуждаемый тест должен быть включен с первым тестом - делится на 32), указав, что нижние 5 бит не равны нулю. Это будет верно для 63 (1000000 - 1 == 1 11111).
Комбинация этих трех тестов будет указывать, делится ли число на 32 или нет. Надеюсь, это ясно, пожалуйста, простите мой странный английский.
Я просто проверял расширяемость на другие полномочия три - следующая программа печатает числа между 383.5 и 388.4, которые делятся на 128.
#include <stdio.h>
#define THRESHOLD 0.11
int main(void) {
int nearly_divisible(float);
int i;
float f = 383.5;
for (i=0; i<50; i++) {
printf("%6.1f %s\n", f, (nearly_divisible(f) ? "true" : "false"));
f += 0.1;
}
return 0;
}
int nearly_divisible(float f) {
// printf(" %f\n", (a - (float)((long) a)));
register long l1, l2;
l1 = (long) (f + THRESHOLD);
l2 = (long) f;
return !(l1 & 127) && (l2 & 127 ? 1 : f - (float) l2 <= THRESHOLD);
}
Кажется, что до сих пор хорошо работает!
Ответ 4
Для чего я собираюсь определить, что число почти делится на другие, не так ли?
Я бы сделал что-то вроде этого:
#define NEARLY_DIVISIBLE 0.1f
bool IsNearlyDivisible(float n1, float n2)
{
float remainder = (fmodf(n1, n2) / n2);
remainder = remainder < 0f ? -remainder : remainder;
remainder = remainder > 0.5f ? 1 - remainder : remainder;
return (remainder <= NEARLY_DIVISIBLE);
}
Ответ 5
Я думаю, это правильно:
bool nearlyDivisible(float num,float div){
float f = num % div;
if(f>div/2.0f){
f=f-div;
}
f=f>0?f:0.0f-f;
return f<0.1f;
}
Ответ 6
Почему бы вам просто не разделить на 32, затем округлить и принять разницу между округленным числом и фактическим результатом?
Что-то вроде (простить непроверенный/псевдокод, нет времени на поиск):
#define NEARLY_DIVISIBLE 0.1f
float result = val / 32.0f;
float nearest_int = nearbyintf(result);
float difference = abs(result - nearest_int);
if( difference < NEARLY_DIVISIBLE )
{
// It nearly divisible
}
Если вы все еще хотите делать проверки сверху и снизу, вы можете удалить абс и проверить, есть ли разницa > 0 или < 0.
Ответ 7
Это не удваивает fmodf дважды.
int main(void)
{
#define NEARLY_DIVISIBLE 0.1f
#define DIVISOR 32.0f
#define ARRAY_SIZE 4
double test_var1[ARRAY_SIZE] = {63.9,64.1,65,63.8};
int i = 54;
double rest;
for(i=0;i<ARRAY_SIZE;i++)
{
rest = fmod(test_var1[i] ,DIVISOR);
if(rest < NEARLY_DIVISIBLE)
{
printf("Number %f max %f larger than a factor of the divisor:%f\n",test_var1[i],NEARLY_DIVISIBLE,DIVISOR);
}
else if( -(rest-DIVISOR) < NEARLY_DIVISIBLE)
{
printf("Number %f max %f less than a factor of the divisor:%f\n",test_var1[i],NEARLY_DIVISIBLE,DIVISOR);
}
}
return 0;
}