Здравствуйте, oziro, Вы писали:
O>(Если кто не понял, это попытка получить значения DBL_EPSILON и FLT_EPSILON, которых по какой-то причине не завезли в дотнет)
Здравствуйте, Qbit86, Вы писали:
Q>Здравствуйте, oziro, Вы писали:
O>>(Если кто не понял, это попытка получить значения DBL_EPSILON и FLT_EPSILON, которых по какой-то причине не завезли в дотнет)
Q>
Здравствуйте, oziro, Вы писали:
O>(Если кто не понял, это попытка получить значения DBL_EPSILON и FLT_EPSILON, которых по какой-то причине не завезли в дотнет)
Здравствуйте, oziro, Вы писали:
O>Привет! O>(Если кто не понял, это попытка получить значения DBL_EPSILON и FLT_EPSILON, которых по какой-то причине не завезли в дотнет)
Просто проводи вычисления с типом float и все промежуточные результаты складывай во временные переменные
static float eps() {
float x = 1/3f, y = 3*x-1;
return 2*y;
}
static float eps1() {
float x = 1,y = 1,t;
for(;;) {
t = x + y;
if (t == x) return y;
y /= 2;
}
}
Здравствуйте, oziro, Вы писали:
_>>Просто проводи вычисления с типом float и все промежуточные результаты складывай во временные переменные O>Шикарно, это колдунство!
только не в временные переменные, а в массив, или в поля класса.
Это не колдунство, это спецификация CLI (врочем, по Кларку она неотличима от магии):
Storage locations for floating-point numbers (statics, array elements, and fields of classes) are of fixed size. The supported storage sizes are float32 and float64. Everywhere else (on the evaluation stack, as arguments, as return types, and as local variables) floating-point numbers are represented using an internal floating-point type. In each such instance, the nominal type of the variable or expression is either float32 or float64, but its value can be represented internally with additional range and/or precision. The size of the internal floating-point representation is implementation-dependent, can vary, and shall have precision at least as great as that of the variable or expression being represented.
Здравствуйте, kov_serg, Вы писали:
_>Просто проводи вычисления с типом float и все промежуточные результаты складывай во временные переменные
А если с полной оптимизацией собрать, не поломается? Там ниже в классы и массивы советуют пихать, в тайной надежде, что оптимизатор туда не дотянется, но тоже ненадёжно. В gcc для этого есть ключ -ffloat-store, а в диезах может быть никому такое не требуется.
Re[3]: Как провести вычисление только в рамках типа float, а не
Здравствуйте, cures, Вы писали:
C>Там ниже в классы и массивы советуют пихать, в тайной надежде, что оптимизатор туда не дотянется, но тоже ненадёжно.
Не дотянется, даже с .net native. Единственно, не ручаюсь, что положение не поменяется где-нибудь через пару релизов.
Кстати, спросить-то забыл. Что за задача такая, что "лишняя" точность мешает?
Re[3]: Как провести вычисление только в рамках типа float, а
Другой вопрос — что вы с этой константой собираетесь делать?
Сравнивать числа так по очевидной причине смысла нет.
UPD особенно с учётом вот этого:
If you create a custom algorithm that determines whether two floating-point numbers can be considered equal, we do not recommend that you base your algorithm on the value of the Epsilon constant to establish the acceptable absolute margin of difference for the two values to be considered equal. (Typically, that margin of difference is many times greater than Epsilon.) For information about comparing two double-precision floating-point values, see Double and Equals(Double).
Platform Notes
On ARM systems, the value of the Epsilon constant is too small to be detected, so it equates to zero. You can define an alternative epsilon value that equals 2.2250738585072014E-308 instead.
Здравствуйте, Sinix, Вы писали:
S>Другой вопрос — что вы с этой константой собираетесь делать?
DBL_EPSILON — это не Double.Epsilon.
DBL_EPSILON the minimum positive number such that 1.0 + DBL_EPSILON != 1.0.
Зря его нет в библиотеке, он равен для double 2.2204460492503131E-16
А алгоритм мой — понятен, по крайней мере мне.
static bool Equal(double a, double b, uint precisionFactor)
{
//in case they are Infinities (then epsilon check does not work)if (a == b)
return true;
double epsilon = precisionFactor * DBL_EPSILON * (Math.Abs(a) + Math.Abs(b));
return Math.Abs(a - b) <= epsilon;
}
precisionFactor берем 16, это 4 бита или примерно 1 наименее значащая десятичная цифра мантиссы. Тем самым довольно универсальный способ сравнения.
Ну, при сравнении с нулем есть нюансы, понятно, сделал специализированные функции.
Но я радикально считаю что Double.Epsilon (и явовскому MIN_NORMAL) не место тут совершенно, не пойму, почему их пихают по всему интернету.
Кстати, решил не считать значения DBL_EPSILON как в первом сообщении, и вбить руками. Меньше кода меньше проблем. Можно вообще тупо 1e-15 задать, по идее тоже норм будет.
Re[5]: Как провести вычисление только в рамках типа float, а
Здравствуйте, oziro, Вы писали:
O>Зря его нет в библиотеке, он равен для double 2.2204460492503131E-16
Я в курсе. Проблема в том, что корректное с математической точки зрения сравнение на практике работает с сюрпризами.
static double GetDblEpsilon()
{
double d = 1;
while (1.0 + d / 2 != 1.0)
{
d = d / 2;
}
return d;
}
private static readonly double DBL_EPSILON = GetDblEpsilon();
static bool Equal(double a, double b, uint precisionFactor)
{
//in case they are Infinities (then epsilon check does not work)if (a == b)
return true;
double epsilon = precisionFactor * DBL_EPSILON * (Math.Abs(a) + Math.Abs(b));
return Math.Abs(a - b) <= epsilon;
}
static void Main(string[] args)
{
var a = 1e-320;
var b = 0d;
Console.WriteLine(a.ToString("R"));
Console.WriteLine(b.ToString("R"));
Console.WriteLine(Equal(a, b, 1024)); // false
Console.WriteLine();
a = 1;
b = 1 * (1 + 1e-15);
Console.WriteLine(a.ToString("R"));
Console.WriteLine(b.ToString("R"));
Console.WriteLine(Equal(a, b, 1024)); // true
}
сравнение с чувствительностью к погрешностям в 10-320, которое при этом игнорит разницу в 10-15 — не та штука, которую стоит иметь в бизнес-коде.
Впрочем, вариант в MSDN:
static bool IsApproximatelyEqual(double value1, double value2, double epsilon)
{
// If they are equal anyway, just return True.if (value1.Equals(value2))
return true;
// Handle NaN, Infinity.if (Double.IsInfinity(value1) | Double.IsNaN(value1))
return value1.Equals(value2);
else if (Double.IsInfinity(value2) | Double.IsNaN(value2))
return value1.Equals(value2);
// Handle zero to avoid division by zerodouble divisor = Math.Max(value1, value2);
if (divisor.Equals(0))
divisor = Math.Min(value1, value2);
return Math.Abs(value1 - value2) / divisor <= epsilon;
}
ещё хуже.
var a = 1e-320;
var b = 0d;
Console.WriteLine(IsApproximatelyEqual(a, b, 0.0000001)); // false
Console.WriteLine(IsApproximatelyEqual(-a, b, 0.0000001)); // true
Угу, сравнивать double могут не только лишь все.
Re[6]: Как провести вычисление только в рамках типа float, а
Т.к. в одной ситуации сравнение 1e-320 c нулем должно дать True, а другом False, в зависимости от самих вычислений. Поэтому я передаю в функцию оценочное значение.
и тогда
False == IsZero(1e-320, 1e-310); // 1e-320 != 0. (1e-310 - напрмер, одно из значений, участвующих в вычислениях)
True == IsZero(1e-320, 1e-100); // 1e-320 == 0 (1e-100 - аналогично, примерное значение, участвующее в вычислених)
Короче, с нулем надо сравнивать с указанной оценкой — это, наверно, всегда. Ну, или делать умолчание на диапазон типа 1e-9 .. 1e9 , но всегда понимать, что тогда оно не будет работать во многих случаях, а это верные грабли, потому что забудут и будет использовать. Поэтому IsZero с доп. параметром-оценкой.
UPD Я, конечно, ошибся. 1e-320 может быть представлено в double Fixed
Здравствуйте, oziro, Вы писали:
O>Короче, с нулем надо сравнивать с указанной оценкой — это, наверно, всегда. Ну, или делать умолчание на диапазон типа 1e-9 .. 1e9 , но всегда понимать, что тогда оно не будет работать во многих случаях, а это верные грабли, потому что забудут и будет использовать. Поэтому IsZero с доп. параметром-оценкой.
Ну да. На практике это всё сводится к эвристикам, которые отлично работают с _конкретными_ сценариями, и фейлятся на всех остальных.
К примеру, для фиксированного диапазона значений с примерно одним порядком величин может сработать абсолютная погрешность где-нибудь в одну миллионную от max value.
Где-то приходится вводить комбо из относительной и абсолютных погрешностей, для каких-то значений в итоге проще может оказаться с decimal работать и округлять до n-го знака после запятой…
Пожалуй, единственное, чего я ещё не видел — схемы сравнения, которая бы устраивала всех
Здравствуйте, Sinix, Вы писали:
S>Кстати, спросить-то забыл. Что за задача такая, что "лишняя" точность мешает?
Это наверное не совсем ко мне, скорее к топикстартеру вопрос
Из своего опыта, видел такое в математических библиотеках, для определения того, с какой точностью в принципе имеет смысл вести вычисления. Например, в QR-алгоритме надо понимать, до какого уровня можно занулять получающиеся поддиагональные элементы.
В нашем коде -ffloat-store используется для (побитовой) воспроизводимости результата на разных платформах, это нужно при проверке работоспособности библиотек и аппаратуры.
Re[5]: Как провести вычисление только в рамках типа float, а не
Здравствуйте, cures, Вы писали:
C>Здравствуйте, Sinix, Вы писали:
S>>Кстати, спросить-то забыл. Что за задача такая, что "лишняя" точность мешает?
C>Это наверное не совсем ко мне, скорее к топикстартеру вопрос
Я сразу скажу: я такого не говорил, ввиду не имел, и не очень понимаю смысл. Так что нет, мне такой вопрос не нужен
А на свои вопросы я ответы получил, всем спасибо!
Re[4]: Как провести вычисление только в рамках типа float, а
Касательно последних двух ссылок: я правильно понимаю, что в своем представлении float\double могут использовать до 80 бит, которые будут обрезаны до 32\64 бит при попытке сохранения в память?
Well, big mistake and the source of an enormous number of questions at SO. The idea was scrapped when Intel implemented the next generation floating point hardware, the XMM and YMM registers are 64-bit. True registers, not a stack. The x64 jitter uses them. Making your program running in 64-bit mode produce different results from when it runs in 32-bit mode. It will take another ten years before that stops hurting
Круть какая, я и не знал, что там все так весело. Особенно легаси x87...
Кодом людям нужно помогать!
Re[5]: Как провести вычисление только в рамках типа float, а
Здравствуйте, Sharov, Вы писали:
S>Касательно последних двух ссылок: я правильно понимаю, что в своем представлении float\double могут использовать до 80 бит, которые будут обрезаны до 32\64 бит при попытке сохранения в память?
Угу. Только "до 80 и более". Мало ли какое железо попадётся
И не при попытке сохранения вообще, а в оговоренные в CLI spec места.
S>Круть какая, я и не знал, что там все так весело.
Ну да. Ещё пару цитат оттуда же:
Rationale: This design allows the CLI to choose a platform-specific high-performance representation for floating-point numbers until they are placed in storage locations. For example, it might be able to leave floating-point variables in hardware registers that provide more precision than a user has requested. At the same time, CIL generators can force operations to respect language-specific rules for representations through the use of conversion instructions.
и
Typical problems: It’s a language choice
Note that with current spec, it’s still a language choice to give ‘predictability’. The language may insert conv.r4 or conv.r8 instructions after every FP operation to get a ‘predictable’ behavior. Obviously, this is really expensive, and different languages have different compromises. C#, for example, does nothing, if you want narrowing, you will have to insert (float) and (double) casts by hand
Ну, или примечание к double.Epsilon:
Platform Notes
On ARM systems, the value of the Epsilon constant is too small to be detected, so it equates to zero. You can define an alternative epsilon value that equals 2.2250738585072014E-308 instead.