Неожиданная потеря точности, когда деление удваивается

Просто используйте term = Worksheets("Sheet1").Cells(1, 1).Value

Sheets коллекция содержит также диаграммы и т. Д., Поэтому лучше использовать worksheets IMO.

Кроме того, не используйте Select, поскольку это плохая практика ( Избегайте использования SELECT или ACTIVATE )

6
задан Bill the Lizard 19 December 2013 в 14:53
поделиться

8 ответов

Могло случиться так использованием DirectX или OpenGL в проекте? Раз так они могут выключить двойную точность, и Вы получите странные результаты.

Можно проверить настройки точности с

std::sqrt(x) * std::sqrt(x)

Результат должен быть достаточно близок к x. Я встретил эту проблему давным-давно, и проведите месяц, проверяя все формулы. Но затем я нашел

D3DCREATE_FPU_PRESERVE
5
ответ дан 8 December 2019 в 05:58
поделиться

Я попробовал плаванием вместо двойного, и я добираюсь 10.845110 в результате. Это все еще выглядит лучше, чем результат madalina.

Править:

Я думаю, что знаю, почему Вы добираетесь, это заканчивается. Если Вы получаете a, b, c и d параметры от где-то в другом месте, и Вы печатаете его, он дает Вам округленные значения. Затем при помещении его в Mathemtacia (или calc ;)) это даст Вам другой результат.

Я пытался изменить немного один из Ваших параметров. Когда я сделал:

double c = 2.7041304;

Я добираюсь 10.845806. Я только добавляю 0.0000004 к c! Таким образом, я думаю, что Ваши "ошибки" не являются ошибками. Распечатайте a, b, c и d с лучшей точностью и затем поместите их в Mathematica.

7
ответ дан 8 December 2019 в 05:58
поделиться

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

Править:

Neil прав в своем комментарии к этому вопросу, я вычислил ответ в использовании VB, Удваивает и получил тот же ответ как mathematica.

3
ответ дан 8 December 2019 в 05:58
поделиться

Следующий код:

#include <iostream>
using namespace std;

double getSlope(double a, double b, double c, double d){
    double slope;
    slope=(d-b)/(c-a);
    return slope;
}

int main( ) {
    double s = getSlope(2.71156, -1.64161, 2.70413, -1.72219);
    cout << s << endl;
}

дает результат 10,8452 с g ++. Как Вы распечатываете результат в своем коде?

5
ответ дан 8 December 2019 в 05:58
поделиться

Лучше Распечатайте аргументы, также. Когда Вы, как я предполагаю, передавая параметры в десятичной записи, Вы потеряете точность для каждого из них. Так как проблема - это, 1/5 является бесконечным рядом в двоичном файле, таким образом, например, 0.2 становится.001001001.... Кроме того, десятичные числа прерываются при преобразовании двоичного плавания в текстовое представление в десятичном числе.

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

1
ответ дан 8 December 2019 в 05:58
поделиться

Patrick, кажется, прав относительно (c-a) быть основной причиной:

d-b =-1,72219 - (-1,64161) =-0,08058

c-a = 2,70413 - 2,71156 =-0,00743

S = (d-b) / (c-a) =-0,08058/-0,00743 = 10,845222

Вы начинаете с шестью точностью цифр посредством вычитания, Вы получаете сокращение к 3 и четырем цифрам. Мое лучшее предположение - то, что Вы освобождаете дополнительную точность, потому что номер-0,00743 не может быть представлен exaclty в двойном. Попытайтесь использовать промежуточные переменные с большей точностью, как это:

double QSweep::getSlope(double a, double b, double c, double d)
{
    double slope;
    long double temp1, temp2;

    temp1 = (d-b);
    temp2 = (c-a);
    slope = temp1/temp2;

    return slope;
}
0
ответ дан 8 December 2019 в 05:58
поделиться

Результаты, которые Вы получаете, согласовываются с арифметикой на 32 бита. Не зная больше о Вашей среде, не возможно советовать, что сделать.

Принятие показанного кода - то, что работает, т.е. Вы ничего не преобразовываете в строки или плавания, затем в C++ нет фиксации. Это за пределами кода, который Вы показали, и зависит от среды.

Поскольку Patrick McDonald и Treb принесли и точность Ваших исходных данных и ошибку на a-c, Я думал, что буду смотреть на это. Одна техника для рассмотрения погрешностей округления является арифметикой интервала, которая делает верхние и нижние границы, которые значение представляет явный (они неявны в числах с плавающей точкой и фиксируются с точностью до представления). Путем обработки каждого значения как верхней и нижней границы, и путем расширения границ ошибкой в представлении (приблизительно x * 2 ^-53 для двойного значения x), Вы получаете результат, который дает нижние и верхние границы на точности значения, принимая во внимание худшие ошибки точности случая.

Например, если Вы имеете значение в диапазоне [1.0, 2.0] и вычитаете из него значение в диапазоне [0.0, 1.0], затем результат должен находиться в диапазоне [ниже (0.0), выше (2.0)], как минимальный результат 1.0-1.0, и максимум 2.0-0.0. below и above эквивалентны полу и потолку, но для следующего представимого значения, а не для целых чисел.

Используя интервалы, которые представляют худший случай, дважды округляющийся:

getSlope(
 a = [2.7115599999999995262:2.7115600000000004144], 
 b = [-1.6416099999999997916:-1.6416100000000002357], 
 c = [2.7041299999999997006:2.7041300000000005888], 
 d = [-1.7221899999999998876:-1.7221900000000003317])
(d-b) = [-0.080580000000000526206:-0.080579999999999665783]
(c-a) = [-0.0074300000000007129439:-0.0074299999999989383218]

to double precision [10.845222072677243474:10.845222072679954195]

Итак, хотя c-a является маленьким по сравнению с c или a, это все еще большое сравненный с двойным округлением, поэтому при использовании худшего вообразимого округления двойной точности затем Вы могли бы положить что значение, чтобы быть точными 12 числам - 10.8452220727. Вы потеряли несколько чисел от двойной точности, но Вы все еще работаете к больше значению, чем своего входа.

Но если исходные данные были только с точностью до значащих цифр числа, то вместо того, чтобы быть двойным значением 2.71156 +/-eps, то входной диапазон будет [2.711555,2.711565], таким образом, Вы получите результат:

getSlope(
 a = [2.711555:2.711565], 
 b = [-1.641615:-1.641605], 
 c = [2.704125:2.704135], 
 d = [-1.722195:-1.722185])
(d-b) = [-0.08059:-0.08057]
(c-a) = [-0.00744:-0.00742]

to specified accuracy [10.82930108:10.86118598]

который является намного более широким диапазоном.

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

С другой стороны, если Ваши исходные данные только известны 6 числам, на самом деле не имеет значения, добираетесь ли Вы 10.8557 или 10.8452. Оба в [10.82930108:10.86118598].

2
ответ дан 8 December 2019 в 05:58
поделиться

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

Это будет иметь некоторые накладные расходы, но вы сможете найти что-то с достаточно гарантированной точностью.

-1
ответ дан 8 December 2019 в 05:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: