Аналог frexp (из С++ math.h) в С#

95
25 ноября 2020, 07:10

Пытаюсь перенести проект с C++ на C#, однако в System.Math C# отсутствует функция разложения на экспоненту и мантиссу (frexp в C++). Существует ли полный аналог в этом языке?

Здесь нашёл написанный вручную метод разложения, только мантисса получается типа long и метод возвращает string: https://stackoverflow.com/questions/389993/extracting-mantissa-and-exponent-from-double-in-c-sharp - в итоге, например, n=10.0 frexp разложит на 0.625*2^4, а эта функция на 5*2^1.

Answer 1

Аналогов нет, так что придется писать самим. Я взял за основу код с ответа, на который вы сослались, и немного доделал его. Там мантисса получается типа лонг потому что внутри бинарного представления там действительно целое чисто стоит на месте мантиссы. В моем коде я дополнительно уменьшаю мантиссу, что бы получить результат согласно спецификации функции frexp:

static void frexp(double value, out double mantissa, out int exponent)
{
    long bits = BitConverter.DoubleToInt64Bits(value);
    bool negative = (bits & (1L << 63)) != 0;
    exponent = (int)((bits >> 52) & 0x7FFL);
    long mantissaLong = bits & 0xFFFFFFFFFFFFFL;
    if (exponent == 0)
    {
        exponent++;
    }
    else
    {
        mantissaLong = mantissaLong | (1L << 52);
    }
    exponent -= 1075;
    if (mantissaLong == 0)
    {
        mantissa = 0;
        exponent = 0;
        return;
    }
    while ((mantissaLong & 1) == 0)
    {
        mantissaLong >>= 1;
        exponent++;
    }
    mantissa = mantissaLong;
    while (mantissa >= 1)
    {
        mantissa /= 2;
        exponent++;
    }
    if (negative)
        mantissa = -mantissa;
}

Несколько тестов:

void TestNum(double value)
{
    double mantissa;
    int exponent;
    frexp(value, out mantissa, out exponent);
    Console.WriteLine($"{value} = {mantissa} * 2^{exponent}");
}
TestNum(0);
TestNum(10);
TestNum(-100);
TestNum(1024);
TestNum(double.MaxValue);

Результат:

0 = 0 * 2^0
10 = 0,625 * 2^4
-100 = -0,78125 * 2^7
1024 = 0,5 * 2^11
1,79769313486232E+308 = 1 * 2^1024

В последнем выводит 1, хотя в дебагере видно, что там 0,99999999999999989. Такова специфика работы double.ToString.

Answer 2

Если на то пошло, можно и напрямую вызвать функцию frexp из CRT:

[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
static extern double frexp(double x,ref int expptr);
static void Main(string[] args)
{
    double x = 1024;            
    int exp=0;
    double m = frexp(x, ref exp);
    Console.WriteLine("{0} = {1} * 2^{2}",x,m,exp);
}

msvcrt.dll - это версия Microsoft CRT, поставляемая с Windows и используемая некоторыми библиотеками в ее составе (любое .NET приложение автоматически грузит ее при старте, поэтому ее использование не привносит никаких дополнительных зависимостей). Поскольку она является недокументированной, возможно, имеет смысл вместо нее использовать версию CRT из определенного Visual C++ Redistrubutable, например msvcr110.dll для Visual C++ 2012.

Answer 3

В дополнение к первому ответу @Zergatul

Код на С#

class Program
{
    static void Main(string[] args)
    {
        Double x = 10;
        Double y = 0;
        y = GetFrexp(x, out Int32 n);
        Console.WriteLine($"{y.ToString("R")} = frexp({x}, 2^{n})");
        Console.ReadKey();
    }
    public static Double GetFrexp(Double value, out Int32 exponent)
    {
        return Frexp(value, out exponent);
    }
    [DllImport("Math.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
    private extern static Double Frexp([MarshalAs(UnmanagedType.R8)] Double value, 
                                       [MarshalAs(UnmanagedType.I4)] out Int32 exponent);
}

Код на C++

MathLibrary.h

#ifdef MATHLIBRARY_EXPORTS
#define MATHLIBRARY_API __declspec(dllexport)
#else
#define MATHLIBRARY_API __declspec(dllimport)
#endif
#include <string>
#include <math.h>
#include <stdio.h>
namespace MathFunc
{
    extern "C" MATHLIBRARY_API double Frexp(double x, int* expptr);
}

MathLibrary.cpp

#include "pch.h"
#include <math.h>
#include <stdio.h>
#include "MathLibrary.h"
#include <stdexcept>
using namespace std;
namespace MathFunc
{
    double Frexp(double x, int* expptr)
    {
         return frexp(x, expptr);
    }
}

Инструкция по созданию dll: Пошаговое руководство. Создание и использование собственной библиотеки динамических ссылок (C++)

READ ALSO
Значение return

Значение return

Я не понимаю, куда и что возвращает оператор returnВ функции main это говорит о завершении программы с кодом 0 (успешно), а что происходит в других...

73
Счетчик css counter

Счетчик css counter

Нужно чтоб счетчик состоял из 3 чисел, например 001, 002, 003 при переходе на 10 элемент было 010

98
Как сдвигать бэкграунд в HTML?

Как сдвигать бэкграунд в HTML?

Есть HTML страница и картинка для бэкграунда (в 3 раза шире ширины экрана)Можно ли ее сдвигать влево и вправо? И каким образом?

106