Обнаружение и настройка на отрицательный ноль

У меня есть код, который был портирован с Java на C ++

// since this point is a vector from (0,0,0), we can just take the
// dot product and compare
double r = point.dot(normal);
return (r>=0.0);

Но в С ++ r может быть +0.0 или же -0.0, когда r равняется -0.0 это не проходит проверку.

Я попытался отрегулировать отрицательный ноль в приведенном ниже коде, но он никогда не попадает в строку DEBUG («Отрицательный ноль»). Но r2 распечатка равна +0.0,

// since this point is a vector from (0,0,0), we can just take the
// dot product and compare
double r = point.dot(normal);
if (std::signbit(r)){
double r2 = r*-1;
DEBUG("r=%f r=%f", r,r2);
if (r2==0.0) {
DEBUG("Negative zero");
r = 0.0; //Handle negative zero
}
}
return (r>=0.0);

Любое предложение?

ИСПЫТАТЕЛЬНЫЙ КОД

DEBUG("point=%s", point.toString().c_str());
DEBUG("normal=%s", normal->toString().c_str());
double r = point.dot(normal);
DEBUG("r=%f", r);
bool b = (r>=0.0);
DEBUG("b=%u", b);

РЕЗУЛЬТАТЫ ИСПЫТАНИЙ:

DEBUG - point=Vector3D[ x=1,y=0,z=0 ]
DEBUG - normal=Vector3D[ x=0,y=-0.0348995,z=0.0348782 ]
DEBUG - r=0.000000
DEBUG - b=1
DEBUG - point=Vector3D[ x=1,y=0,z=0 ]
DEBUG - normal=Vector3D[ x=-2.78269e-07,y=0.0174577,z=-0.0174391 ]
DEBUG - r=-0.000000
DEBUG - b=0

GCC:

Target: x86_64-linux-gnu
--enable-languages=c,c++,fortran,objc,obj-c++
--prefix=/usr
--program-suffix=-4.6
--enable-shared
--enable-linker-build-id
--with-system-zlib
--libexecdir=/usr/lib
--without-included-gettext
--enable-threads=posix
--with-gxx-include-dir=/usr/include/c++/4.6
--libdir=/usr/lib
--enable-nls
--with-sysroot=/
--enable-clocale=gnu
--enable-libstdcxx-debug
--enable-libstdcxx-time=yes
--enable-gnu-unique-object
--enable-plugin
--enable-objc-gc
--disable-werror
--with-arch-32=i686
--with-tune=generic
--enable-checking=release
--build=x86_64-linux-gnu
--host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)

ФЛАГИ:

CXXFLAGS += -g -Wall -fPIC

ОТВЕТ:

Я использовал ответ @ amit, чтобы сделать следующее.

return (r>=(0.0-std::numeric_limits<double>::epsilon()));

Который, кажется, работает.

5

Решение

Ну, общее предложение при использовании doubles вспоминает, что они не точны. Таким образом, если равенство важно — обычно рекомендуется использовать какой-либо фактор толерантности.

В твоем случае:

if (|r - 0.0| >= EPSILON)

где EPSILON ваш фактор толерантности, даст истину, если r не 0.0, по крайней мере EPSILON интервал.

10

Другие решения

В некоторых более старых системах (например, до IEE754) вы можете найти проверки на равенство 0 и отрицательный 0:

if (a == 0.0) // when a==-0.0, fails

Вы можете обойти это, добавив 0.0 к значению перед сравнением:

if ((a+0.0) == 0.0) // when a == -0.0, succeeds

Однако я хотел бы предупредить, что комбинации аппаратного и программного обеспечения, которые действительно требуют этого, довольно необычно. В прошлый раз я должен был это сделать на мэйнфрейме Control Data. Даже там, это возникло только при несколько необычных обстоятельствах: компилятор Фортрана позволял генерировать отрицательные нули и знал, как компенсировать их в сравнениях. Компилятор Pascal сгенерировал код для преобразования отрицательных нулей в нормальные нули как часть вычисления.

Поэтому, если вы написали подпрограмму на Фортране и вызвали ее из Паскаля, вы можете столкнуться с этой проблемой и предотвратить ее, как указано выше, добавив 0.0 перед выполнением сравнения.

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

6

Предположительно, вы имели в виду что-то вроде if (r2==-0.0), Тем не менее, и отрицательный 0, и положительный 0 будут сравниваться одинаково. Для всех намерений и целей нет никакой разницы между этими двумя. Вам, вероятно, не нужно иметь особый случай для отрицательного 0. Ваше сравнение r >= 0 должен быть правдой для отрицательного или положительного 0.

3

Рассматривать:

#include <stdio.h>
#include <iostream>
using namespace std;

int main()
{
double const x = -2.78269e-07;

printf( "printf: x=%f\n", x );
cout << "cout: x=" << x << endl;
}

С результатом (с использованием Visual C ++ 11.0):

[D: \ DEV \ тест]
> cl foo.cpp
foo.cpp

[D: \ DEV \ тест]
> Foo
printf: x = -0.000000
cout: x = -2,78269e-007

[D: \ DEV \ тест]
> _

Это кажется очень похожим на таинственный результат в вопросе.

Это мое взвешенное мнение, что крякает как результат вопроса, выглядит как результат вопроса а также болтовня, как результат вопроса.

Итак, я считаю, что не показанный вычислительный код дал значение 2.78269e-007,


Итак, в заключение, это было, по-видимому, только описанное поведение относительно «отрицательного нуля», сравнивающего неравное с нулем, которое было нестандартным. В действительности, по-видимому, нет отрицательного нуля, просто очень небольшое отрицательное значение. Который с заданным форматом вывода представлен как все нулевые цифры со знаком минус впереди.

1

Это просто очень маленькое отрицательное число, которое консоль не смогла напечатать для вас.
Вы можете попробовать проверить это в зависимости от вашей точности, чтобы вы могли заменить его на чистый ноль.

std::cout << std::setprecision(7) << (abs(value) < 0.0000005f ? 0 : value);

уведомление как я добавил 7 мест к плавающей точности, как я указал с std::setprecision(),

Это зависит от того, насколько точно вы хотите напечатать ваши поплавки / двойные.

0
По вопросам рекламы [email protected]