Реализован ли Math.Log таким образом, чтобы избежать потери точности для журнала (1 + x)? - PullRequest
0 голосов
/ 25 апреля 2018

Я перевожу некоторый код C # на C ++, который содержит использование Math.Log(1 + x), где x может быть очень маленьким значением, близким к нулю. Я натолкнулся на рекомендацию (в C ++) использовать std::log1p для работы с log(1 + x), где x близко к нулю, то есть , чтобы избежать потери точности .

Реализован ли метод C # /. NET Math.Log таким образом, чтобы избежать потери точности? Я не смог найти аналогичную функцию Math.Log1p(), на которую есть ссылка в .NET API.

Ответы [ 3 ]

0 голосов
/ 25 апреля 2018

Нет, Math.Log () использует функцию библиотеки языка C (), чтобы выполнить свою работу. log1p () фактически включен в библиотеку CRT, которую использует CLR, но он не предоставляется через платформу. Это можно исправить с помощью объявления pinvoke:

using System;
using System.Runtime.InteropServices;

public static class Math {
    public static double Log1p(double arg) {
        if (arg < -1.0) throw new ArgumentException();
        return log1p(arg);
    }

    [DllImport("msvcr120_clr0400.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern double log1p(double arg);
}

Если вам нужно настроить таргетинг на версию .NET менее 4.0, подумайте об изменении объявления DllImport на использование «ucrtbase.dll».

Возможно, примечательно, что я не смог получить согласованное представление об этом коде. Нацелившись на x64 и на C #, и на C на своем ноутбуке, я получил ~ 140 наносекунд за звонок. Но увидел большую разницу при таргетинге на x86, 225 против 40. Большая разница, у меня нет хорошего объяснения этому.

0 голосов
/ 25 апреля 2018
double VerySmall = .0000000000000002;
double TooSmall = .0000000000000001;
Console.WriteLine("{0} {1} {2}", Math.Log(1 + VerySmall), Math.Log(1 + TooSmall), log1p(TooSmall));

static double log1p(double x)
      => Math.Abs(x) > 1e-4 ? Math.Log(1.0 + x) : (-0.5 * x + 1.0) * x;

В .NET Framework 4.6.1, работающем в Windows, вывод этого кода:

2.22044604925031E-16 0 1E-16
0 голосов
/ 25 апреля 2018

Рассматривая справочную реализацию , Log() просто вызывает внешнюю функцию Log(), которая, как я полагаю, является просто логарифмической функцией набора команд.

Популярная библиотека Accord.Math действительно имеет функцию log1p, которая является довольно простой и может быть реализована с помощью следующего однострочного:

static double log1p(double x)
    => Math.Abs(x) > 1e-4 ? Math.Log(1.0 + x) : (-0.5 * x + 1.0) * x;
...