Перевести мантиссу и экспоненту в двойную - PullRequest
8 голосов
/ 19 января 2012

В приложении с очень высокой производительностью мы находим, что ЦП может вычислять длинную арифметику значительно быстрее, чем с двойными. Однако в нашей системе было определено, что нам никогда не нужно более 9 десятичных знаков точности. Таким образом, мы использовали long для всей арифметики с плавающей запятой с понятной точностью до 9 пунктов.

Однако в некоторых частях системы удобнее работать с двойниками. Таким образом, мы должны преобразовать длинное значение, которое предполагает 9 десятичных разрядов, в двойное.

Мы находим, что простое взятие длинных и деление на 10 до степени 9 или умножение на 1, деленное на 10 до степени 9 дает неточные представления в двойном выражении.

Чтобы решить эту проблему, мы используем Math.Round(value,9), чтобы дать точные значения.

Однако, Math.Round() ужасно медленен для производительности.

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

Мы узнали онлайн, как исследовать биты двойного числа, чтобы получить мантиссу и показатель степени, но сложно понять, как изменить это, чтобы взять мантиссу и показатель степени и изготовить двойное число, используя биты.

Есть предложения?

[Test]
public unsafe void ChangeBitsInDouble()
{
    var original = 1.0D;
    long bits;
    double* dptr = &original;
    //bits = *(long*) dptr;
    bits = BitConverter.DoubleToInt64Bits(original);
    var negative = (bits < 0);
    var exponent = (int) ((bits >> 52) & 0x7ffL);
    var mantissa = bits & 0xfffffffffffffL;
    if( exponent == 0)
    {
        exponent++;
    }
    else
    {
        mantissa = mantissa | (1L << 52);
    }
    exponent -= 1075;

    if( mantissa == 0)
    {
        return;
    }

    while ((mantissa & 1) == 0)
    {
        mantissa >>= 1;
        exponent++;
    }

    Console.WriteLine("Mantissa " + mantissa + ", exponent " + exponent);

}

Ответы [ 2 ]

1 голос
/ 19 января 2012

Вы не должны использовать масштабный коэффициент 10 ^ 9, вместо этого вы должны использовать 2 ^ 30.

0 голосов
/ 19 января 2012

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

Также не ясно, еслион может работать с преднамеренно упрощенной формулой, поскольку неясно, какой максимальный диапазон вам нужен, поэтому округление становится неизбежным.

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

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

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

Если вы находитесь в точке, где вам нужно покрыть большинство случаев, охватываемых IEEE 754, или даже значительную их часть,тогда ты будешь делать вещи медленнее.

Я бы порекомендовал либо оставаться с тем, что у вас есть, перемещая случаи, когда double удобнее держать в любом случае долго, несмотря на неудобства, либо, если необходимо, использовать decimal.Вы можете легко создать decimal из long с помощью:

private static decimal DivideByBillion (long l)
{
  if(l >= 0)
   return new decimal((int)(l & 0xFFFFFFFF), (int)(uint)(l >> 32), 0, false, 9);
  l = -l;
  return new decimal((int)(l & 0xFFFFFFFF), (int)(uint)(l >> 32), 0, true, 9);
}

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

...