2012-04-24 5 views
11

У меня есть программа, которая находит пути в графе и выводит совокупный вес. Все ребра на графике имеют индивидуальный вес от 0 до 100 в виде поплавка с не более чем двумя знаками после запятой.0 + 0 + 0 ... + 0! = 0

В Windows/Visual Studio 2010 для конкретного пути, состоящего из ребер с весом 0, он выводит правильный общий вес 0. Однако в Linux/GCC программа говорит, что путь имеет вес 2.35503e-38. У меня было много опыта с сумасшедшими ошибками, вызванными поплавками, но когда 0 + 0 когда-либо равнялось бы чему-либо, кроме 0?

Единственное, что я могу думать об этом, заключается в том, что программа обрабатывает некоторые из весов как целые числа и использует неявное принуждение, чтобы добавить их в общую. Но 0 + 0.0f все равно равно 0.0f! В качестве быстрого решения я уменьшаю итоговое значение до 0 при менее 0,00001, и этого достаточно для моих нужд. Но что это такое?

Примечание: Я 100% уверен, что ни один из весов в графе не превышает диапазон я уже говорил, и что все веса в данном пути равны 0.

EDIT: Выработать , Я попробовал как считывать веса из файла, так и устанавливать их в коде вручную как равное 0.0f. Никакая другая операция не выполняется над ними, кроме добавления их в общую.

+19

Можете ли вы построить минимальный тестовый чехол? –

+0

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

+0

Я бы рекомендовал вам изучить определение машинного эпсилона. Похоже, это то, что вызывает вашу ошибку здесь. – andre

ответ

11

Потому что это число с плавающей запятой IEEE, и оно не совсем равно нулю.

http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

+3

Но вопрос в том, почему? (как в, почему это не ноль?) Однако это невозможно ответить (пока), потому что нам нужно увидеть тестовый файл из OP. –

+0

Я изучил плавающие точки совсем немного в моем курсе Numerical Analysis, но я никогда не встречал случая, когда 0 + 0 должно быть чем-то другим, кроме 0. Я никогда не делаю никаких других операций над ним. –

+0

http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm Почему 0 не 0?Я думаю, что в разделе «Нули» сказано, что 0 равно 0 и точно представлено в правилах IEEE 754. –

3

Это может быть, что ваши поплавки, содержащие значения «0.0f» не являются на самом деле 0.0f (битовое представление 0x00000000), но очень, очень небольшое число, которое оценивает приблизительно 0.0. Из-за того, как спецификация IEEE754 определяет представления с плавающей точкой, если у вас есть, например, очень маленькая мантисса и показатель степени 0, тогда как она не равна абсолютному 0, она будет округлена до 0. Однако, если вы добавите эти числа вместе достаточно количество раз, очень небольшое количество будет накапливаться в значение, которое в конечном итоге станет ненулевым.

Вот пример случай, который дает иллюзию 0 быть отличен от нуля:

float f = 0.1f/1000000000; 
printf("%f, %08x\n", f, *(unsigned int *)&f); 
float f2 = f * 10000; 
printf("%f, %08x\n", f2, *(unsigned int *)&f2); 

При назначении литералов к вашим переменным и складывая их, хотя, возможно, что либо компилятор не перевод 0 в 0x0 в память. Если это так, и это все еще происходит, то также возможно, что ваше аппаратное обеспечение процессора имеет ошибку, связанную с превращением 0s в ненулевое значение при выполнении операций ALU, которые, возможно, скрипели от их усилий по проверке.

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

+0

Нет, это не мое дело. Я НЕ РАЗМЕЩАЮ. Я только назначаю 0 и 0.0f переменным, а затем добавляю их. –

+0

Я приводил пример того, что может вызвать иллюзию. Конечно, многие ситуации могут вызвать нечто подобное. –

+0

Правильно, я просто не думал, что добавление константы 0 может привести к этому. –

5

[...] в виде поплавка, насчитывающего не более чем 2 десятичных знаков.

Там нет такого понятия, как поплавок с в большинстве 2 знаков после запятой. Поплавки почти всегда представлены как двоичное число с плавающей запятой (дробная двоичная мантисса и целочисленная экспонента). Так много (большинство) чисел с 2 десятичными знаками невозможно точно представить.

Например, 0.20f может выглядеть как невинный и круглой фракции, но

printf("%.40f\n", 0.20f); 

напечатает: 0,2000000029802322387695312500000000000000.

См., У него нет 2 десятичных знаков, у него есть 26 !!!

Естественно, для большинства практических применений разница в незначительности. Но если вы сделаете некоторые расчеты, вы можете в конечном итоге увеличить ошибку округления и сделать ее видимой, особенно около 0.

+0

Я знал об этом для ненулевых значений, но моя проблема возникает при работе с 0s. Однако для большого количества нулей, которые немного отключаются, возможно, это вызывает проблему, как указано в другом ответе. –

+0

И откуда вы знаете, что имеете дело с 0? Являются ли они константами компилятора? Чтение из файла? – rodrigo

+0

Я пытался использовать константы и читать из файла. –

Смежные вопросы