Неожиданное поведение Math.Floor (двойной) и Math.Ceiling (двойной) - PullRequest
10 голосов
/ 29 марта 2012

Этот вопрос касается порога, при котором Math.Floor(double) и Math.Ceiling(double) решают дать вам предыдущее или следующее целочисленное значение.Я был встревожен, обнаружив, что порог, кажется, не имеет ничего общего с Double.Epsilon, который является наименьшим значением, которое может быть представлено с двойным.Например:

double x = 3.0;
Console.WriteLine( Math.Floor( x - Double.Epsilon ) );  // expected 2, got 3
Console.WriteLine( Math.Ceiling( x + Double.Epsilon) ); // expected 4, got 3

Даже умножение Double.Epsilon на справедливый бит не сработало:

Console.WriteLine( Math.Floor( x - Double.Epsilon*1000 ) );  // expected 2, got 3
Console.WriteLine( Math.Ceiling( x + Double.Epsilon*1000) ); // expected 4, got 3

С некоторыми экспериментами я смог определить, что пороггде-то около 2.2E-16, что очень мало, но НАМНОГО больше, чем Double.Epsilon.

Причина, по которой возник этот вопрос, заключается в том, что я пытался вычислить количество цифр в числе по формуле var digits = Math.Floor( Math.Log( n, 10 ) ) + 1.Эта формула не работает для n=1000 (на которую я наткнулся совершенно случайно), поскольку Math.Log( 1000, 10 ) возвращает число, равное 4.44E-16 от его действительного значения.(Позже я обнаружил, что встроенный Math.Log10(double) обеспечивает гораздо более точные результаты.)

Не следует ли привязывать порог к Double.Epsilon или, если нет, не документировать порог (Я не смог найти упоминаний об этом в официальной документации MSDN)?

Ответы [ 3 ]

15 голосов
/ 29 марта 2012

Не следует ли привязывать порог к Double.Epsilon

Нет.

Представляемые двойники неравномерно распределены по действительным числам.Близко к нулю есть много представимых значений.Но чем дальше от нуля, тем дальше друг от друга представимые двойники.Для очень больших чисел, даже если добавить 1 к двойному, вы не получите новое значение.

Поэтому пороговое значение, которое вы ищете, зависит от того, насколько велико ваше число.Это не константа.

10 голосов
/ 29 марта 2012

Значение Double.Epsilon равно 4,94065645841247e-324 .Добавление или вычитание этого значения к 3 приводит к 3 из-за способа работы с плавающей запятой.

A double имеет 53 бита мантиссы, поэтому наименьшее значение, которое вы можете добавить, будет иметь какое-либо влияние,примерно в 2 ^ 53 раза меньше вашей переменной.Итак, что-то около 1е-16 звучит примерно правильно (порядок величины).

Итак, чтобы ответить на ваш вопрос: «порога» нет;floor и ceil просто действуют по своим аргументам именно так, как вы ожидаете.

3 голосов
/ 29 марта 2012

Это будет скорее махать рукой, чем ссылками на спецификации, но я надеюсь, что мое "интуитивное объяснение" вам подходит.

Эпсилон представляет наименьшую величину, которая может быть представлена, которая отличается от нуля. Учитывая, что у мантиссы и показатель двойного, это будет очень мало - подумайте 10 ^ -324. Между десятичной точкой и первой ненулевой цифрой более трехсот нулей.

Однако Double представляет примерно 14-15 цифр точности. Это все еще оставляет 310 цифр нулей между Epsilon и целыми числами .

Double с фиксированной битовой длины. Если вам действительно нужны вычисления с произвольной точностью, вам следует использовать библиотеку с произвольной точностью. И будьте готовы к тому, что он будет значительно медленнее - для представления всех 325 цифр, которые необходимы для хранения числа, такого как 2+epsilon, потребуется примерно в 75 раз больше памяти на число. Это хранилище не является бесплатным, и его вычисление, безусловно, не может осуществляться на полной скорости процессора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...