Интересным фактом является то, что в каждом опубликованном ответе исправлена ошибка в предложенном вами коде, но только один вызвал, что они это сделали.
Двоичная с плавающей запятойчисла имеют ошибку представления при работе с любой величиной, которая не является долей точной степени двойки.(«3,0 / 4,0» - представимая дробь, потому что нижняя часть имеет степень двойки; «1,0 / 10,0» - нет.)
Поэтому, когда вы говорите:
for(i = 0; i < 10; i++)
{
myArray[i] = increment;
increment += 0.1;
}
Выфактически не увеличивают «приращение» на 1,0 / 10,0.Вы увеличиваете его на ближайшую представимую дробь, которая имеет точную степень двух в нижней части.Так что на самом деле это эквивалентно:
for(i = 0; i < 10; i++)
{
myArray[i] = increment;
increment += (exactly_one_tenth + small_representation_error);
}
Итак, каково значение приращения десятого ?Очевидно, это 10 * (exactly_one_tenth + small_representation_error)
, что, очевидно, равно exactly_one + 10 * small_representation_error
. Вы увеличили размер ошибки представления на десять.
Каждый раз, когда вы многократно складываете два числа с плавающей запятой, каждое последующее добавление немного увеличивает общую ошибку представления суммы и это буквально добавляет к потенциально большой ошибке.В некоторых случаях, когда вы суммируете тысячи или миллионы небольших чисел, ошибка может стать намного больше, чем фактическая сумма .
Гораздо лучшим решением будет сделать то, что сделали все остальные. Пересчитать дробь из целых чисел каждый раз .Таким образом, каждый результат получает свою собственную небольшую ошибку представления;он не накапливает ошибки представления ранее вычисленных результатов.