@helpmeplease

Как реализовать алгоритм вывода дробной части числа в двоичной системе счисления на языке си?

Как реализовать алгоритм вывода дробной части числа в двоичной системе счисления на языке си? Заранее спасибо!
  • Вопрос задан
  • 155 просмотров
Пригласить эксперта
Ответы на вопрос 2
saboteur_kiev
@saboteur_kiev
software engineer
В двоичной системе дробная часть не предусмотрена.
Можете самостоятельно решить, сколько бит отдать на дробную часть, сколько на целую, и с этим работать.
Ответ написан
gzhegow
@gzhegow
aka "ОбнимиБизнесмена"
На Си не подскажу, но ты без труда перепишешь код ниже.

"mb_{что-то}" в пхп - работа с мультибайтовыми строками, UTF-8.
"bc{что-то}" в пхп - работа с числами как со строками без float потерь

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

Тесты:

<?php

$res = _base_convert_floor(100, '0ABCDEFGHIJKLMNOPQRSTUVWXYZ', '01');
$res2 = _base_convert_floor($res, '01', '0ABCDEFGHIJKLMNOPQRSTUVWXYZ', 0);
var_dump($res); // string(1) "D"
var_dump($res2); // string(3) "100"

$res = _base_convert_frac('.75', '01', '0123456789');
$res2 = _base_convert_frac($res, '0123456789', '01');
var_dump($res); // string(2) ".11"
var_dump($res2); // string(2) ".75"

$res = _base_convert('-10.75', '01', '0123456789');
$res2 = _base_convert($res, '0123456789', '01');
var_dump($res); // string(7) "-1010.11"
var_dump($res2); // string(5) "-10.75"

$res = _base_convert_floor('КУКУЁПТА', '0123456789', 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ');
$res2 = _base_convert_floor($res, 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ', '0123456789');
var_dump($res); // string(1) "495086673543"
var_dump($res2); // string(1) "КУКУЁПТА"

// @gzhegow > бонус, колонка "как в экселе"
$res = _base_convert_floor(27, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', '0123456789', -1);
$res2 = _base_convert_floor($res, '0123456789', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 0, -1);
var_dump($res); // string(2) "AA"
var_dump($res2); // string(2) "27"


Исходник:

<?php

function _base_convert_floor($floor,
    string $baseCharsTo = null, string $baseCharsFrom = '0123456789',
    int $baseShiftTo = 0, int $baseShiftFrom = 0
) : string
{
    $floor = strval($floor);

    $baseTo = mb_strlen($baseCharsTo);
    $baseFrom = mb_strlen($baseCharsFrom);

    if (! $len = mb_strlen($floor)) return '';
    if ($baseTo < 2) throw new \InvalidArgumentException('Conversion assume `baseCharsTo` to be at least 2 letters: ' . $baseCharsTo);
    if ($baseFrom < 2) throw new \InvalidArgumentException('Conversion assume `baseCharsFrom` to be at least 2 letters: ' . $baseCharsFrom);

    $baseCharsFromIndex = array_flip(mb_str_split($baseCharsFrom));

    for ( $i = 0; $i < $len; $i++ ) {
        if (! isset($baseCharsFromIndex[ mb_substr($floor, $i, 1) ])) {
            throw new \InvalidArgumentException('The `floor` contains char outside `baseCharsFrom`: ' . $floor[ $i ]);
        }
    }

    $baseChars10 = '0123456789';

    if ($baseCharsFrom === $baseCharsTo) {
        return $floor;

    } elseif ($baseCharsFrom === $baseChars10) {
        $result = [];

        $div = $floor;
        if (0 > bccomp(bcadd($div, $baseShiftTo, 0), 0, 0)) {
            throw new \RuntimeException('Unable to convert cause of `baseShiftTo`: ' . $div);
        }

        do {
            $div = bcadd($div, $baseShiftTo, 0);

            $mod = bcmod($div, $baseTo, 0);
            $div = bcdiv($div, $baseTo, 0);

            $result[] = mb_substr($baseCharsTo, (int) $mod, 1);
        } while ( bccomp($div, 0, 1) );

        $result = implode('', array_reverse($result));

    } elseif ($baseCharsTo === $baseChars10) {
        $result = '0';

        for ( $i = 1; $i <= $len; $i++ ) {
            $idx = $baseCharsFromIndex[ mb_substr($floor, $i - 1, 1) ];
            $idx = bcsub($idx, $baseShiftFrom, 0);

            $pow = bcpow($baseFrom, $len - $i);
            $sum = bcmul($idx, $pow, 0);

            $result = bcadd($result, $sum, 0);
        }

    } else {
        $result = $floor;
        $result = _base_convert_floor($result, $baseChars10, $baseCharsFrom, 0, $baseShiftFrom);
        $result = _base_convert_floor($result, $baseCharsTo, $baseChars10, $baseShiftTo, 0);
    }

    return $result;
}

function _base_convert_frac(string $frac,
    string $baseCharsTo = null, string $baseCharsFrom = '0123456789',
    int $scale = null
) : string
{
    $frac = ltrim($frac, '.');

    $baseTo = mb_strlen($baseCharsTo);
    $baseFrom = mb_strlen($baseCharsFrom);

    if (! $len = mb_strlen($frac)) return '';
    if ($baseTo < 2) throw new \InvalidArgumentException('Conversion assume `baseCharsTo` to be at least 2 letters: ' . $baseCharsTo);
    if ($baseFrom < 2) throw new \InvalidArgumentException('Conversion assume `baseCharsFrom` to be at least 2 letters: ' . $baseCharsFrom);

    $baseCharsFromIndex = array_flip(mb_str_split($baseCharsFrom));

    for ( $i = 0; $i < $len; $i++ ) {
        if (! isset($baseCharsFromIndex[ mb_substr($frac, $i, 1) ])) {
            throw new \InvalidArgumentException('The `frac` contains char outside `baseCharsFrom`: ' . $frac[ $i ]);
        }
    }

    $scale = $scale ?? $len;
    $scale = max($scale, 0);

    $baseChars10 = '0123456789';

    if ($baseCharsFrom === $baseCharsTo) {
        return $frac;

    } elseif ($baseCharsFrom === $baseChars10) {
        $result = [];

        $mul = bcadd('0.' . $frac, 0, $scale);

        $limit = $scale;
        while ( $limit-- ) {
            $mul = bcmul($mul, $baseTo, $scale);
            $floor = bcadd($mul, 0, 0);

            $mul = bcsub($mul, $floor, $scale);

            $result[] = mb_substr($baseCharsTo, (int) $floor, 1);

            if (0 === bccomp($mul, 0, $scale)) break;
        }

        $result = implode('', $result);

    } elseif ($baseCharsTo === $baseChars10) {
        $result = '0';

        for ( $i = 1; $i <= $len; $i++ ) {
            $idx = $baseCharsFromIndex[ mb_substr($frac, $i - 1, 1) ];

            $pow = bcpow($baseFrom, -$i, $scale);
            $sum = bcmul($idx, $pow, $scale);

            $result = bcadd($result, $sum, $scale);
        }

        $result = explode('.', $result, 2)[ 1 ] ?? '0';

    } else {
        $result = $frac;
        $result = _base_convert_frac($result, $baseChars10, $baseCharsFrom);
        $result = _base_convert_frac($result, $baseCharsTo, $baseChars10);
    }

    return '.' . $result;
}

function _base_convert($num,
    string $baseCharsTo = null, string $baseCharsFrom = '0123456789',
    int $scale = null,
    int $baseShiftTo = 0, int $baseShiftFrom = 0
)
{
    $isNegative = 0 === mb_strpos($num, '-');

    $numAbs = ltrim($num, '-');
    [ $numFloor, $numFrac ] = explode('.', $numAbs, 2) + [ '0', '' ];

    $scale = $scale ?? mb_strlen($numFrac);

    $result = [];
    if (mb_strlen($numFloor)) $result[] = _base_convert_floor($numFloor, $baseCharsTo, $baseCharsFrom, $baseShiftTo, $baseShiftFrom);
    if (mb_strlen($numFrac)) $result[] = _base_convert_frac($numFrac, $baseCharsTo, $baseCharsFrom, $scale);

    $result = implode('', $result);

    if ($isNegative) {
        $result = '-' . $result;
    }

    return $result;
}
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы