У меня есть подпрограмма, целью которой является кодирование числа с плавающей запятой как целого числа (затем оно помещается в поток байтов). В процессе информация теряется. Подпрограмма нарезает вывод на приращения по 3,33.

Итак, разрыв между 0 и 3,33 - это своего рода странный случай, потому что он не равен нулю и не равен 3,33. Я решил (не полностью произвольно), что любой ненулевой плавающий ввод должен быть округлен до 3,33 (и аналогично 3,34 должно увеличиться до 6,66 и т. Д.).

Это все хорошо. Код выглядит следующим образом:

function EncodeFloatingPoint (Value : Long_Float) return Integer is
    Intermediate_Value : Integer;
    Return_Value : Integer := 0;
begin
    -- converts float into 3.33 increments

    -- case 1, positive or zero 
    if (Value >= 0.0) then
        --Ensures rounding away from zero
        Intermediate_Value := Integer((Value * 100.0 + 332.99) / 333.0); 

    -- case 2, negative through -3.33 (up to, but not including zero)
    else

        -- Code omitted, is not problematic
    end if;

    Return_Value := Intermediate_Value;

    return Return_Value;
end

Проблема возникает, когда я передаю значение 0.0 в свой код во время модульного тестирования. При пошаговом выполнении преобразование приводит к значению 3.33, а не к ожидаемому 0.0, и мой модульный тест не проходит. Однако, когда я перехожу в консоль GDB и набираю Print Integer((0.0 * 100.0 + 332.99) / 333.0), результат будет 0, как и ожидалось (заменяю 0.0 на Value). Я могу подтвердить, что значение Value распечатывается до 0,0.

Что могло происходить? Есть ли более простой способ обеспечить округление от нуля при преобразовании его обратно в целое число?

0
theMayer 13 Мар 2018 в 01:54

2 ответа

Лучший ответ

Если вы заглянете в Справочное руководство по языку, вы увидит, что типы с плавающей запятой имеют функцию атрибута 'Ceiling, которая выдает «наименьшее (наиболее отрицательное) целое значение, большее или равное X» (переданный параметр).

Так что попробуйте: Rounded := Floating_Point_Type'Ceiling (Not_Rounded)

2
Jacob Sparre Andersen 13 Мар 2018 в 07:49

Вы вычисляете 332.99 / 333.0, что составляет около 0,99997. Преобразование вещественных типов в целочисленные циклы [ARM 4.6 (33)], поэтому вы получаете 1. Я полагаю, вы интерпретируете это как 3.33. Вероятно, вам понадобится специальный нулевой случай:

if Value > Long_Float'Pred (0.0) and Value < Long_Float'Succ (0.0) then
   return 0;
end if;

(Обратите внимание, что скобки вокруг условия оператора «if» не нужны.)

Вам также может пригодиться функция атрибута 'Remainder. Вы должны быть знакомы со всеми атрибутами с плавающей запятой в ARM A.5.3 (действительно, со всей стандартной библиотекой в ARM A).

Похоже, что вы создаете свой собственный тип с фиксированной точкой с небольшим значением 3,33. Почему бы не позволить языку сделать это за вас?

Result_Delta : constant := 3.33;

type Result_Value is delta Result_Delta
   range Integer'First * Result_Delta .. Integer'Last * Result_Delta
   with Small => Result_Delta;
5
Jeffrey R. Carter 13 Мар 2018 в 09:09